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.biojava.nbio.ontology;
023
024import org.biojava.nbio.ontology.utils.AssertionFailure;
025
026import java.util.*;
027
028
029
030
031/**
032 * An ontology.
033 *
034 * <p>This is just a set of Term objects, and a set of
035 * Triple objects describing relationships between these terms.
036 * This class does not itself contain any reasoning functionality. Ontology is
037 * a collection of facts, or axioms.</p>
038 *
039 * @author Thomas Down
040 * @author Matthew Pocock
041 *
042 * @since 1.4
043 * @see org.biojavax.ontology.ComparableOntology
044 */
045
046public interface Ontology  {
047
048
049        /**
050         * Return the name of this ontology
051         * @return the name of the ontology
052         */
053
054        public String getName();
055
056        /** Set the name for this ontology
057         *
058         * @param name - the name
059         *
060         */
061        public void setName(String name);
062
063        /**
064         * Return a human-readable description of this ontology, or the empty
065         * string if none is available
066         * @return the description of the term
067         */
068
069        public String getDescription();
070
071        /** set the description of this ontology
072         *
073         * @param description
074         */
075        public void setDescription(String description);
076
077
078
079        /**
080         * Return all the terms in this ontology
081         * @return a Set of all Terms of the ontology.
082         */
083
084        public Set<Term> getTerms();
085
086        /**
087         * Fetch the term with the specified name.
088         * @param name the name of the term
089         *
090         * @return The term named <code>name</code>
091         * @throws NoSuchElementException if no term exists with that name
092         */
093
094        public Term getTerm(String name) throws NoSuchElementException;
095
096        /**
097         * Return all triples from this ontology which match the supplied
098         * pattern.  If any of the parameters of this method are <code>null</code>,
099         * they are treated as wildcards.
100         *
101         * @param subject The subject to search for, or <code>null</code>
102         * @param object The object to search for, or <code>null</code>
103         * @param predicate The relationship to search for, or <code>null</code>.
104         * @return a Set of triples
105         */
106
107        public Set<Triple> getTriples(Term subject, Term object, Term predicate);
108
109        /**
110         * Return the associated OntologyOps.
111         *
112         * This method should be implemented by ontology
113         * implementors to allow OntoTools
114         * to get optimized access to some usefull ontology operations. It is not
115         * intended that users will ever invoke this. A sensible dumb implementation
116         * of this would return a per-ontology instance of DefaultOps.
117         *
118         * @return the OntologyOps instance associated with this instance.
119         */
120
121        public OntologyOps getOps();
122
123        /**
124         * Create a new term in this ontology.
125         *
126         * @param name The name of the term (must be unique))
127         * @throws IllegalArgumentException if either <code>name</code> or
128         *         <code>description</code> is <code>null</code>, or violates
129         *         some other constraint of this implementation.
130         * @throws AlreadyExistsException if a term of this name already exists
131         * @return The newly created term.
132         * @throws ChangeVetoException
133         */
134
135        public Term createTerm(String name)
136                        throws
137                        AlreadyExistsException,
138
139                        IllegalArgumentException;
140
141        /**
142         * Create a new term in this ontology.
143         *
144         * @param name The name of the term (must be unique)
145         * @param description A human-readable description (may be empty)
146         * @throws IllegalArgumentException if either <code>name</code> or
147         *         <code>description</code> is <code>null</code>, or violates
148         *         some other constraint of this implementation.
149         * @throws AlreadyExistsException if a term of this name already exists
150         * @return The newly created term.
151         * @throws ChangeVetoException
152         */
153
154        public Term createTerm(String name, String description)
155                        throws
156                        AlreadyExistsException,
157
158                        IllegalArgumentException;
159
160        /**
161         * Create a new term in this ontology.
162         *
163         * @param name The name of the term (must be unique)
164         * @param description A human-readable description (may be empty)
165         * @param synonyms Some synonyms for this term.
166         * @throws IllegalArgumentException if either <code>name</code> or
167         *         <code>description</code> is <code>null</code>, or violates
168         *         some other constraint of this implementation.
169         * @throws AlreadyExistsException if a term of this name already exists
170         * @return The newly created term.
171         * @throws ChangeVetoException
172         */
173
174        public Term createTerm(String name, String description, Object[] synonyms)
175                        throws
176                        AlreadyExistsException,
177
178                        IllegalArgumentException;
179
180        /**
181         * Create a new term in this ontology that is used as a variable.
182         *
183         * @param name The name of the term (must be unique)
184         * @param description A human-readable description (may be empty)
185         * @throws IllegalArgumentException if either <code>name</code> or
186         *         <code>description</code> is <code>null</code>, or violates
187         *         some other constraint of this implementation.
188         * @throws AlreadyExistsException if a term of this name already exists
189         * @return The newly created term.
190         * @throws ChangeVetoException
191         */
192
193        public Variable createVariable(String name, String description)
194                        throws
195                        AlreadyExistsException,
196
197                        IllegalArgumentException;
198
199        /**
200         * Create a view of a term from another ontology.  If the requested term
201         * has already been imported under that name, this method returns the existing
202         * RemoteTerm object. If the term that is being imported is itself a
203         * RemoteTerm instance then first unwrap the term back to the orriginal
204         * term it represents and then produce a RemoteTerm from that. If the term
205         * being imported orriginated from this ontology, then return that term
206         * unaltered.
207         *
208         * @param t  the Term to import
209         * @param localName  the local name to import it under, optionally null
210         * @return a Term
211         * @throws ChangeVetoException
212         * @throws IllegalArgumentException
213         */
214
215        public Term importTerm(Term t, String localName)
216                        throws
217
218                        IllegalArgumentException;
219
220        /**
221         * Creates a new Triple.
222         *
223         * @param subject     the subject Term
224         * @param object      the object Term
225         * @param predicate    the predicate Term
226         * @param name        the name of the triple, or null
227         * @param description the description of the triple, or null
228         * @return  a new Triple over these three terms
229         * @throws AlreadyExistsException if a triple already exists with the same
230         *      subject, object and predicate, regardless of the name and description
231         * @throws ChangeVetoException
232         * @throws NullPointerException if subject, object or predicate are null
233         * @throws IllegalArgumentException if subject, object or predicate are not all
234         *      from the same ontology
235         */
236        public Triple createTriple(Term subject, Term object, Term predicate, String name, String description)
237                        throws
238                        AlreadyExistsException
239                        ;
240
241        /**
242         * See if a triple exists in this ontology
243         * @param subject
244         * @param object
245         * @param predicate
246         * @return true if contained
247         */
248
249        public boolean containsTriple(Term subject, Term object, Term predicate);
250
251        /**
252         * Remove a term from an ontology, together with all triples which refer to it.
253         * @param t
254         * @throws ChangeVetoException
255         */
256
257        public void deleteTerm(Term t) ;
258
259        /**
260         * Determines if this ontology currently contains a term named <code>name</code>
261         * @param name
262         * @return true is contained
263         */
264
265        public boolean containsTerm(String name);
266
267        /**
268         * A basic in-memory implementation of an ontology
269         *
270         * @author Thomas Down
271         * @author Matthew Pocock
272         * @since 1.3
273         */
274
275        // AP: I am setting name and description to public changeable fields
276        // e.g during parsing of an .obo file we don't know them when the ontology is instanciated
277        public final class Impl
278
279        implements Ontology, java.io.Serializable {
280                /**
281                 *
282                 */
283                private static final long serialVersionUID = -8064461497813727957L;
284                private final Map<String,Term> terms;
285                private final Set<Triple> triples;
286                private final Map<Term, Set<Triple>> subjectTriples;
287                private final Map<Term, Set<Triple>> objectTriples;
288                private final Map<Term, Set<Triple>> relationTriples;
289                private final Map<Term,RemoteTerm> remoteTerms;
290                private final Set<Term> localRemoteTerms;
291
292                private /*final*/ String name;
293                private /*final*/ String description;
294                private final OntologyOps ops;
295
296                {
297                        terms            = new HashMap<String, Term>();
298                        triples          = new HashSet<Triple>();
299                        subjectTriples   = new HashMap<Term, Set<Triple>>();
300                        objectTriples    = new HashMap<Term, Set<Triple>>();
301                        relationTriples  = new HashMap<Term, Set<Triple>>();
302                        remoteTerms      = new HashMap<Term, RemoteTerm>();
303                        localRemoteTerms = new HashSet<Term>();
304                }
305
306                public Impl(String name, String description) {
307                        this.name = name;
308                        this.description = description;
309                        ops = new DefaultOps() {
310                                /**
311                                 *
312                                 */
313                                private static final long serialVersionUID = -2135777733685713181L;
314
315                                @Override
316                                public Set<Term> getRemoteTerms() {
317                                        return localRemoteTerms;
318                                }
319                        };
320                }
321
322                @Override
323                public String getName() {
324                        return name;
325                }
326
327                @Override
328                public String getDescription() {
329                        return description;
330                }
331
332
333                @Override
334                public void setDescription(String description){
335                        this.description = description;
336                }
337
338                @Override
339                public Set<Term> getTerms() {
340                        return new HashSet<Term>(terms.values());
341                }
342
343                @Override
344                public Term getTerm(String name)
345                                throws NoSuchElementException
346                                {
347                        Term t = terms.get(name);
348                        if (t == null) {
349                                throw new NoSuchElementException("No term named '" + name + "'");
350                        } else {
351                                return terms.get(name);
352                        }
353                                }
354
355                @Override
356                public Set<Triple> getTriples(Term subject, Term object, Term predicate) {
357                        if(subject != null && subject.getOntology() != this) {
358                                throw new IllegalArgumentException("Subject is not in this ontology: " + subject + " " + this);
359                        }
360
361                        if(object != null && object.getOntology() != this) {
362                                throw new IllegalArgumentException("Object is not in this ontology: " + object + " " + this);
363                        }
364
365                        if(predicate != null && predicate.getOntology() != this) {
366                                throw new IllegalArgumentException("Predicate is not in this ontology: " + predicate + " " + this);
367                        }
368
369                        if (subject != null) {
370                                return filterTriples( subjectTriples.get(subject), null, object, predicate);
371                        } else if (object != null) {
372                                return filterTriples( objectTriples.get(object), subject, null, predicate);
373                        } else if (predicate != null) {
374                                return filterTriples( relationTriples.get(predicate), subject, object, null);
375                        } else {
376                                return filterTriples(triples, subject, object, predicate);
377                        }
378                }
379
380                private Set<Triple> filterTriples(Set<Triple> base, Term subject, Term object, Term predicate) {
381                        if (base == null) {
382                                return Collections.EMPTY_SET;
383                        } else if (subject == null && object == null && predicate == null) {
384                                return Collections.unmodifiableSet(new HashSet<Triple>(base));
385                        }
386
387                        Set<Triple> retval = new HashSet<Triple>();
388                        for (Iterator<Triple> i = base.iterator(); i.hasNext(); ) {
389                                Triple t = i.next();
390                                if (subject != null && t.getSubject() != subject) {
391                                        continue;
392                                }
393                                if (object != null && t.getObject() != object) {
394                                        continue;
395                                }
396                                if (predicate != null && t.getPredicate() != predicate) {
397                                        continue;
398                                }
399                                retval.add(t);
400                        }
401                        return retval;
402                }
403
404                private void addTerm(Term t)
405                                throws AlreadyExistsException, IllegalArgumentException
406                                {
407                        if (terms.containsKey(t.getName())) {
408                                throw new AlreadyExistsException("Ontology " + getName() + " already contains " + t.toString());
409                        }
410
411
412                        terms.put(t.getName(), t);
413
414                                }
415
416                @Override
417                public Term createTerm(String name)
418                                throws AlreadyExistsException, IllegalArgumentException
419                                {
420                        Term t = new Term.Impl(this, name);
421                        addTerm(t);
422                        return t;
423                                }
424
425                @Override
426                public Term createTerm(String name, String description)
427                                throws AlreadyExistsException, IllegalArgumentException
428                                {
429                        Term t = new Term.Impl(this, name, description);
430                        addTerm(t);
431                        return t;
432                                }
433
434                @Override
435                public Term createTerm(String name, String description, Object[] synonyms)
436                                throws AlreadyExistsException, IllegalArgumentException
437                                {
438                        Term t = new Term.Impl(this, name, description, synonyms);
439                        addTerm(t);
440                        return t;
441                                }
442
443                @Override
444                public Variable createVariable(String name, String description)
445                                throws
446                                AlreadyExistsException,
447
448                                IllegalArgumentException {
449                        Variable var = new Variable.Impl(this, name, description);
450                        addTerm(var);
451                        return var;
452                }
453
454                public OntologyTerm createOntologyTerm(Ontology o)
455                                throws AlreadyExistsException
456                                {
457                        OntologyTerm ot = new OntologyTerm.Impl(this, o);
458                        addTerm(ot);
459                        return ot;
460                                }
461
462
463                @Override
464                public Term importTerm(Term t, String name)
465                                throws IllegalArgumentException
466                                {
467                        // unpack any potential indirection - belt & braces
468                        while(t instanceof RemoteTerm) {
469                                t = ((RemoteTerm) t).getRemoteTerm();
470                        }
471
472                        if(t.getOntology() == this) {
473                                return t;
474                        }
475
476                        RemoteTerm rt = remoteTerms.get(t);
477                        if (rt == null) {
478                                rt = new RemoteTerm.Impl(this, t, name);
479                                try {
480                                        addTerm(rt);
481                                } catch (AlreadyExistsException e) {
482                                        throw new AssertionFailure("This term can not exist", e);
483                                }
484                                if(name == null) {
485                                        remoteTerms.put(t, rt);
486                                }
487                                localRemoteTerms.add(rt);
488                        }
489                        return rt;
490                                }
491
492                @Override
493                public void deleteTerm(Term t)
494
495                {
496                        String name = t.getName();
497                        if (terms.get(name) != t) {
498                                return; // Should this be an exception?
499                        }
500
501                        terms.remove(name);
502                        if(t instanceof Triple) {
503                                removeTriple((Triple) t);
504                        }
505
506                }
507
508                @Override
509                public boolean containsTerm(String name) {
510                        return terms.containsKey(name);
511                }
512
513                private boolean containsTerm(Term t) {
514                        return (terms.get(t.getName()) == t);
515                }
516
517                @Override
518                public boolean containsTriple(Term subject, Term object, Term predicate) {
519                        if(!(subject.getOntology() == this)) return false;
520                        if(!(object.getOntology() == this)) return false;
521                        if(!(predicate.getOntology() == this)) return false;
522
523                        return triples.contains(new Triple.Impl(subject, object, predicate));
524                }
525
526                @Override
527                public Triple createTriple(Term subject,
528                                Term object,
529                                Term predicate,
530                                String name,
531                                String description)
532                                                throws
533                                                AlreadyExistsException,
534                                                IllegalArgumentException,
535                                                NullPointerException,
536                                                IllegalArgumentException
537                                                {
538                        Triple t = new Triple.Impl(subject, object, predicate, name, description);
539                        if (!containsTerm(subject)) {
540                                throw new IllegalArgumentException("Ontology " + getName() + " doesn't contain " + subject);
541                        }
542                        if (!containsTerm(predicate)) {
543                                throw new IllegalArgumentException("Ontology " + getName() + " doesn't contain " + predicate);
544                        }
545                        if (!containsTerm(object)) {
546                                throw new IllegalArgumentException("Ontology " + getName() + " doesn't contain " + object);
547                        }
548                        if (triples.contains(t)) {
549                                throw new AlreadyExistsException("Ontology " + getName() + " already contains " + t.toString());
550                        }
551
552
553                        addTerm(t);
554                        addTriple(t);
555
556                        return t;
557                                                }
558
559                private void addTriple(Triple t) {
560                        triples.add(t);
561                        pushTriple(subjectTriples, t.getSubject(), t);
562                        pushTriple(objectTriples, t.getObject(), t);
563                        pushTriple(relationTriples, t.getPredicate(), t);
564                }
565
566                private void pushTriple(Map<Term,Set<Triple>> m, Term key, Triple t) {
567                        Set<Triple> s = m.get(key);
568                        if (s == null) {
569                                s = new HashSet<Triple>();
570                                m.put(key, s);
571                        }
572                        s.add(t);
573                }
574
575                private void removeTriple(Triple t) {
576                        triples.remove(t);
577                        pullTriple(subjectTriples, t.getSubject(), t);
578                        pullTriple(objectTriples, t.getObject(), t);
579                        pullTriple(relationTriples, t.getPredicate(), t);
580                }
581
582                private void pullTriple(Map<Term, Set<Triple>> subjectTriples2, Term key, Triple t) {
583                        Set<Triple> s = subjectTriples2.get(key);
584                        if (s != null) {
585                                s.remove(t);
586                        }
587                }
588
589                @Override
590                public OntologyOps getOps() {
591                        return ops;
592                }
593
594                @Override
595                public String toString() {
596                        return "ontology: " + getName();
597                }
598
599                @Override
600                public void setName(String name) {
601                        this.name=name;
602
603                }
604        }
605}
606