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.Annotation;
025
026import java.util.Arrays;
027import java.util.Set;
028import java.util.TreeSet;
029
030
031/**
032 * A triple in an ontology.  This is two terms and a relationship between
033 * them, similar to RDF and other similar logic systems.
034 *
035 * <p>
036 * For documentation purposes, a Triple may provide a name. However, a Triple
037 * may also be named as "(subject, object, predicate)" if no specific name is
038 * provided.
039 * </p>
040 *
041 * @author Thomas Down
042 * @author Matthew Pocock
043 * @since 1.4
044 */
045
046public interface Triple
047extends Term {
048        /**
049         * Return the subject term of this triple
050         * @return the subject term
051         */
052
053        public Term getSubject();
054
055        /**
056         * Return the object term of this triple.
057         * @return the object term
058         */
059
060        public Term getObject();
061
062        /**
063         * Return a Term which defines the type of relationship between the subject and object terms.
064         * @return the predicate
065         */
066
067        public Term getPredicate();
068
069        /**
070         * The hashcode for a Triple.
071         *
072         * <p>This <em>must</em> be implemented as:
073         * <pre>
074         * return getSubject().hashCode() +
075         * 31 * getObject().hashCode() +
076         * 31 * 31 * getPredicate().hashCode();
077         * </pre>
078         * If you do not implement hashcode in this way then you have no guarantee
079         * that your Triple objects will be found in an ontology and that they will
080         * not be duplicated.
081         */
082        @Override
083        public int hashCode();
084
085        /**
086         * Check to see if an object is an equivalent Triple.
087         *
088         * <p>
089         * Two triples are equivalent if they have the same subject, object and
090         * predicate fields.
091         * <pre>{@code
092         * if (! (o instanceof Triple)) {
093         *     return false;
094         * }
095         * Triple to = (Triple) o;
096         * return to.getSubject() == getSubject() &&
097         *        to.getObject() == getObject() &&
098         *        to.getPredicate() == getPredicate();
099         * }</pre>
100         * If you do not implement equals in this way then you have no guarantee
101         * that your Triple objects will be found in an ontology and that they will
102         * not be duplicated.
103         */
104        @Override
105        public boolean equals(Object obj);
106
107        /**
108         * Basic in-memory implementation of a Triple in an ontology
109         *
110         * This can be used to implement Ontology.createTriple
111         */
112        public static final class Impl
113
114        implements Triple, java.io.Serializable {
115                /**
116                 *
117                 */
118                private static final long serialVersionUID = 3807331980372839221L;
119                private final Term subject;
120                private final Term object;
121                private final Term predicate;
122                private /*final*/ String name;
123                private /*final*/ String description;
124                private Set<Object> synonyms;
125
126                public Impl(Term subject, Term object, Term predicate) {
127                        this(subject, object, predicate, null, null, null);
128                }
129
130                public Impl(Term subject, Term object, Term predicate, Object[] synonyms) {
131                        this(subject, object, predicate, null, null, synonyms);
132                }
133
134                public Impl(Term subject,
135                                Term object,
136                                Term predicate,
137                                String name,
138                                String description) {
139                        this(subject,object,predicate,name,description,null);
140                }
141
142                public Impl(Term subject,
143                                Term object,
144                                Term predicate,
145                                String name,
146                                String description,
147                                Object[] synonyms)
148                {
149                        if (subject == null) {
150                                throw new NullPointerException("Subject must not be null");
151                        }
152                        if (object == null) {
153                                throw new NullPointerException("Object must not be null");
154                        }
155                        if (predicate == null) {
156                                throw new NullPointerException("predicate must not be null");
157                        }
158
159                        if(
160                                        subject.getOntology() != object.getOntology() ||
161                                        subject.getOntology() != predicate.getOntology()
162                        ) {
163                                throw new IllegalArgumentException(
164                                                "All terms must be from the same ontology: " +
165                                                subject.getOntology().getName() + ", " +
166                                                object.getOntology().getName() + ", " +
167                                                predicate.getOntology().getName());
168                        }
169
170                        if(description == null) {
171                                description = "";
172                        }
173
174                        this.subject = subject;
175                        this.object = object;
176                        this.predicate = predicate;
177                        this.name = name;
178                        this.description = description;
179
180                        this.synonyms = new TreeSet<>();
181                        if (synonyms!=null) this.synonyms.addAll(Arrays.asList(synonyms));
182                }
183
184                @Override
185                public void addSynonym(Object synonym) {
186                        this.synonyms.add(synonym);
187                }
188
189                @Override
190                public void removeSynonym(Object synonym) {
191                        this.synonyms.remove(synonym);
192                }
193
194                @Override
195                public Object[] getSynonyms() {
196                        return this.synonyms.toArray();
197                }
198
199                @Override
200                public String getName() {
201                        if(name == null) {
202                                name = predicate + "(" + subject + ", " + object + ")";
203                        }
204                        return name;
205                }
206
207                @Override
208                public String getDescription() {
209                        return description;
210                }
211                @Override
212                public void setDescription(String desc){
213                        this.description = desc;
214                }
215
216                @Override
217                public Ontology getOntology() {
218                        return subject.getOntology();
219                }
220
221                @Override
222                public Term getSubject() {
223                        return subject;
224                }
225
226                @Override
227                public Term getObject() {
228                        return object;
229                }
230
231                @Override
232                public Term getPredicate() {
233                        return predicate;
234                }
235
236                @Override
237                public Annotation getAnnotation() {
238                        return Annotation.EMPTY_ANNOTATION;
239                }
240
241                /**
242                 * Two triples are equal if all their fields are identical.
243                 */
244
245                @Override
246                public boolean equals(Object o) {
247                        if (! (o instanceof Triple)) {
248                                return false;
249                        }
250                        Triple to = (Triple) o;
251                        return to.getSubject().equals(getSubject()) &&
252                        to.getObject().equals(getObject()) &&
253                        to.getPredicate().equals(getPredicate());
254                }
255
256                @Override
257                public int hashCode() {
258                        return getSubject().hashCode() +
259                        31 * getObject().hashCode() +
260                        31 * 31 * getPredicate().hashCode();
261                }
262
263                @Override
264                public String toString() {
265                        if (getName().length() > 0)
266                                return getName();
267                        return subject + " " + predicate + " " + object;
268                }
269        }
270}