View Javadoc
1   /*
2    * The baseCode project
3    *
4    * Copyright (c) 2013 University of British Columbia
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    * http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
12   * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
13   * specific language governing permissions and limitations under the License.
14   */
15  package ubic.basecode.ontology.model;
16  
17  import java.util.Collection;
18  import java.util.HashSet;
19  import java.util.Set;
20  
21  import org.apache.commons.lang3.ObjectUtils;
22  
23  import com.hp.hpl.jena.ontology.AllValuesFromRestriction;
24  import com.hp.hpl.jena.ontology.ConversionException;
25  import com.hp.hpl.jena.ontology.Individual;
26  import com.hp.hpl.jena.ontology.OntClass;
27  import com.hp.hpl.jena.ontology.OntProperty;
28  import com.hp.hpl.jena.ontology.OntResource;
29  import com.hp.hpl.jena.ontology.Restriction;
30  import com.hp.hpl.jena.ontology.SomeValuesFromRestriction;
31  import com.hp.hpl.jena.rdf.model.Property;
32  import com.hp.hpl.jena.rdf.model.RDFNode;
33  import com.hp.hpl.jena.rdf.model.Resource;
34  import com.hp.hpl.jena.rdf.model.ResourceFactory;
35  import com.hp.hpl.jena.rdf.model.Statement;
36  import com.hp.hpl.jena.rdf.model.StmtIterator;
37  import com.hp.hpl.jena.util.iterator.ExtendedIterator;
38  
39  /**
40   * Represents a class in an ontology
41   *
42   * @author Paul
43   */
44  public class OntologyTermImpl extends AbstractOntologyResource implements OntologyTerm {
45  
46      private static final String HAS_ALTERNATE_ID = "http://www.geneontology.org/formats/oboInOwl#hasAlternativeId";
47      private static final String NOTHING = "http://www.w3.org/2002/07/owl#Nothing";
48      private static Set<String> REJECT_PARENT_URI = new HashSet<>();
49  
50      /**
51       *
52       */
53      private static final long serialVersionUID = 1L;
54  
55      /**
56       * Should has_proper_part be used to indicate additional parent/child relations.
57       */
58      private static final boolean USE_PROPER_PART_RESTRICTIONS = true;
59      static {
60          REJECT_PARENT_URI.add( "http://www.ifomis.org/bfo/1.1/snap#IndependentContinuant" );
61          REJECT_PARENT_URI.add( "http://www.ifomis.org/bfo/1.1/snap#Continuant" );
62          REJECT_PARENT_URI.add( "http://www.ifomis.org/bfo/1.1/snap#MaterialEntity" );
63  
64          // anatomical entity
65          REJECT_PARENT_URI.add( "http://ontology.neuinfo.org/NIF/BiomaterialEntities/NIF-GrossAnatomy.owl#birnlex_6" );
66      }
67  
68      private String label = null;
69      private String localName = null;
70  
71      private transient OntClass ontResource = null;
72  
73      public OntologyTermImpl( OntClass resource ) {
74          this.ontResource = resource;
75          if ( ontResource != null ) {
76              this.label = ontResource.getLabel( "EN" );
77              if ( this.label == null ) this.label = ontResource.getLabel( null );
78              this.localName = ontResource.getLocalName();
79          }
80      }
81  
82      @Override
83      public boolean equals( Object obj ) {
84          if ( this == obj ) return true;
85  
86          if ( !super.equals( obj ) ) return false;
87          if ( getClass() != obj.getClass() ) return false;
88  
89          final OntologyTermImpl that = ( OntologyTermImpl ) obj;
90          if ( this.getUri() != null ) {
91              return ObjectUtils.equals( this.getUri(), that.getUri() );
92          }
93          return ObjectUtils.equals( this.getTerm(), that.getTerm() );
94      }
95  
96      @Override
97      public Collection<String> getAlternativeIds() {
98          Collection<String> results = new HashSet<>();
99  
100         Property alternate = ResourceFactory.createProperty( HAS_ALTERNATE_ID );
101         for ( StmtIterator it = this.ontResource.listProperties( alternate ); it.hasNext(); ) {
102             Statement statement = it.next();
103             results.add( statement.asTriple().getMatchObject().getLiteralLexicalForm() );
104         }
105 
106         return results;
107 
108     }
109 
110     @Override
111     public Collection<AnnotationProperty> getAnnotations() {
112         Collection<AnnotationProperty> annots = new HashSet<>();
113         StmtIterator iterator = ontResource.listProperties();
114         // this is a little slow because we have to go through all statements for the term.
115         while ( iterator.hasNext() ) {
116             Statement state = iterator.next();
117             OntResource res = state.getPredicate().as( OntResource.class );
118             if ( res.isAnnotationProperty() ) {
119                 com.hp.hpl.jena.ontology.AnnotationProperty p = res.asAnnotationProperty();
120                 RDFNode n = state.getObject();
121                 annots.add( new AnnotationPropertyImpl( p, n ) );
122             }
123         }
124         return annots;
125     }
126 
127     @Override
128     public Collection<OntologyTerm> getChildren( boolean direct ) {
129         Collection<OntologyTerm> result = new HashSet<>();
130         getChildren( direct, result );
131         return result;
132     }
133 
134     /*
135      * (non-Javadoc)
136      *
137      * @see ubic.gemma.ontology.OntologyTerm#getComment()
138      */
139     @Override
140     public String getComment() {
141         String comment = this.ontResource.getComment( null );
142         return comment == null ? "" : comment;
143     }
144 
145     /*
146      * (non-Javadoc)
147      *
148      * @see ubic.gemma.ontology.OntologyTerm#getIndividuals()
149      */
150     @Override
151     public Collection<OntologyIndividual> getIndividuals() {
152         return getIndividuals( true );
153     }
154 
155     /**
156      * @param  direct
157      * @return
158      */
159     @Override
160     public Collection<OntologyIndividual> getIndividuals( boolean direct ) {
161         Collection<OntologyIndividual> inds = new HashSet<>();
162         ExtendedIterator<? extends OntResource> iterator = this.ontResource.listInstances( direct );
163         while ( iterator.hasNext() ) {
164             Individual i = ( Individual ) iterator.next();
165             inds.add( new OntologyIndividualImpl( i ) );
166         }
167         return inds;
168     }
169 
170     @Override
171     public String getLabel() {
172         return label;
173     }
174 
175     @Override
176     public String getLocalName() {
177         return this.localName;
178     }
179 
180     @Override
181     public Object getModel() {
182         return ontResource.getModel();
183     }
184 
185     @Override
186     public Collection<OntologyTerm> getParents( boolean direct ) {
187         Collection<OntologyTerm> result = new HashSet<>();
188         this.getParents( direct, result );
189         return result;
190     }
191 
192     /**
193      *
194      */
195     @Override
196     public Collection<OntologyRestriction> getRestrictions() {
197         /*
198          * Remember that restrictions are superclasses.
199          */
200         Collection<OntologyRestriction> result = new HashSet<>();
201         ExtendedIterator<OntClass> iterator = ontResource.listSuperClasses( false );
202         while ( iterator.hasNext() ) {
203             OntClass c = iterator.next();
204             Restriction r = null;
205             try {
206                 r = c.asRestriction();
207                 result.add( RestrictionFactory.asRestriction( r ) );
208             } catch ( Exception e ) {
209 
210             }
211 
212         }
213 
214         // Check superclasses for any ADDITIONAL restrictions.
215         iterator = ontResource.listSuperClasses( false );
216         while ( iterator.hasNext() ) {
217             OntClass c = iterator.next();
218 
219             try {
220                 c.asRestriction(); // throw it away, we already processed it above.
221             } catch ( Exception e ) {
222                 // not a restriction, but a superclass that might have restrictions
223                 ExtendedIterator<OntClass> supClassesIt = c.listSuperClasses( false );
224                 loop: while ( supClassesIt.hasNext() ) {
225                     OntClass sc = supClassesIt.next();
226                     Restriction sr = null;
227                     try {
228                         sr = sc.asRestriction();
229 
230                         // only add it if the class doesn't already have one.
231                         OntologyRestriction candidateRestriction = RestrictionFactory.asRestriction( sr );
232                         for ( OntologyRestriction restr : result ) {
233                             if ( restr.getRestrictionOn().equals( candidateRestriction.getRestrictionOn() ) )
234                                 continue loop;
235                         }
236                         result.add( candidateRestriction );
237 
238                     } catch ( Exception ex ) {
239                         // superclass isn't a restriction.
240                     }
241                 }
242             }
243 
244         }
245 
246         return result;
247     }
248 
249     /*
250      * (non-Javadoc)
251      *
252      * @see ubic.gemma.analysis.ontology.OntologyTerm#getTerm()
253      */
254     @Override
255     public String getTerm() {
256         String res = null;
257         if ( this.label != null ) {
258             res = this.label;
259         } else if ( this.localName != null ) {
260             res = localName;
261         } else if ( this.getUri() != null ) {
262             res = this.getUri();
263         } else {
264             res = ontResource.toString();
265         }
266         return res;
267     }
268 
269     @Override
270     public String getUri() {
271         return this.ontResource.getURI();
272     }
273 
274     @Override
275     public int hashCode() {
276         if ( ontResource == null ) {
277             log.warn( "ontResource is null in hashCode()" );
278             return 0;
279         }
280         // assert this.getUri() != null : "No URI for " + this.getTerm();
281         if ( this.getUri() != null ) {
282             return this.getUri().hashCode();
283         }
284         return this.getTerm().hashCode();
285     }
286 
287     /*
288      * (non-Javadoc)
289      *
290      * @see ubic.gemma.analysis.ontology.OntologyTerm#isRoot()
291      */
292     @Override
293     public boolean isRoot() {
294         return !this.ontResource.listSuperClasses( true ).hasNext();
295     }
296 
297     /*
298      * (non-Javadoc)
299      *
300      * @see ubic.basecode.ontology.model.OntologyTerm#isTermObsolete()
301      */
302     @Override
303     public boolean isTermObsolete() {
304 
305         Collection<OntologyTerm> parentsOntologyTerm = getParents( false );
306 
307         // this limit of 1 was because obsolete terms are connected right to the root. But ... not guaranteed.
308         // / if ( parentsOntologyTerm.size() == 1 ) {
309 
310         for ( OntologyTerm parentOntologyTerm : parentsOntologyTerm ) {
311 
312             if ( parentOntologyTerm.getLocalName() != null
313                     && parentOntologyTerm.getLocalName().equalsIgnoreCase( "ObsoleteClass" ) ) {
314                 return true;
315             }
316 
317             // debug code.
318             if ( parentOntologyTerm.getUri() == null ) {
319                 // log.warn( "URI for " + parentOntologyTerm + " was null" ); // not a problem.
320                 continue;
321             }
322 
323             if ( parentOntologyTerm.getUri() != null
324                     && parentOntologyTerm.getUri().equalsIgnoreCase(
325                             "http://bioontology.org/projects/ontologies/birnlex#_birnlex_retired_class" )
326                     || parentOntologyTerm
327                             .getUri()
328                             .equalsIgnoreCase(
329                                     "http://ontology.neuinfo.org/NIF/Backend/BIRNLex_annotation_properties.owl#_birnlex_retired_class" ) ) {
330                 return true;
331             }
332         }
333         // }
334 
335         StmtIterator iterator = ontResource.listProperties();
336         // this is a little slow because we have to go through all statements for the term.
337         while ( iterator.hasNext() ) {
338             Statement state = iterator.next();
339             if ( state.getPredicate() == null ) continue;
340             OntResource res = state.getPredicate().as( OntResource.class );
341             if ( res.isAnnotationProperty() ) {
342                 com.hp.hpl.jena.ontology.AnnotationProperty p = res.asAnnotationProperty();
343                 RDFNode n = state.getObject();
344 
345                 if ( p.getLocalName().equalsIgnoreCase( "deprecated" ) ) {
346                     if ( n.toString().indexOf( "true" ) != -1 ) {
347                         return true;
348                     }
349                     break;
350                 }
351             }
352         }
353         return false;
354     }
355 
356     @Override
357     public String toString() {
358         String res = null;
359         if ( this.getTerm() != null ) {
360             res = this.getTerm();
361             if ( this.localName != null && !this.getTerm().equals( this.localName ) ) {
362                 res = res + " [" + this.localName + "]";
363             }
364         } else if ( this.localName != null ) {
365             res = localName;
366         } else if ( this.getUri() != null ) {
367             res = this.getUri();
368         } else {
369             res = ontResource.toString();
370         }
371         return res;
372     }
373 
374     protected OntologyTerm fromOntClass( OntClass ontClass ) {
375         return new OntologyTermImpl( ontClass );
376     }
377 
378     /**
379      * @param direct
380      * @param work
381      */
382     private void getChildren( boolean direct, Collection<OntologyTerm> work ) {
383 
384         // get children by recursion, don't rely on the jena api.
385         ExtendedIterator<OntClass> iterator = ontResource.listSubClasses( true );
386         while ( iterator.hasNext() ) {
387             OntClass c = iterator.next();
388             // URI can be null if the ont is a bnode (no idea what it is, but we have to handle this)
389             // some reasoners will infer owl#Nothing as a subclass of everything
390             if ( c.getURI() == null || c.getURI().equals( NOTHING ) ) continue;
391 
392             if ( USE_PROPER_PART_RESTRICTIONS && c.isRestriction() ) {
393 
394                 Restriction restriction = c.asRestriction();
395 
396                 OntProperty onProperty = restriction.getOnProperty();
397 
398                 if ( !onProperty.getURI().equals( "http://www.obofoundry.org/ro/ro.owl#has_proper_part" ) ) {
399                     continue;
400                 }
401 
402                 Resource r = getRestrictionValue( restriction );
403                 if ( r == null ) continue;
404 
405                 OntologyTerm child = fromOntClass( ( OntClass ) r );
406 
407                 // avoid risk of endless regression.
408                 if ( !work.contains( child ) ) {
409                     work.add( child );
410                     if ( !direct ) ( ( OntologyTermImpl ) child ).getChildren( false, work );
411                 }
412             } else {
413                 OntologyTerm child = this.fromOntClass( c );
414                 work.add( child );
415                 if ( !direct ) ( ( OntologyTermImpl ) child ).getChildren( false, work );
416             }
417             // log.info( c );
418         }
419 
420         if ( USE_PROPER_PART_RESTRICTIONS ) {
421             ExtendedIterator<OntClass> sciterator = this.ontResource.listSuperClasses( true );
422             while ( sciterator.hasNext() ) {
423                 OntClass c = sciterator.next();
424                 if ( !c.isRestriction() ) {
425                     continue;
426                 }
427 
428                 Restriction restriction = c.asRestriction();
429 
430                 try {
431                     OntProperty onProperty = restriction.getOnProperty();
432                     if ( !onProperty.getURI().equals( "http://www.obofoundry.org/ro/ro.owl#has_proper_part" ) ) {
433                         continue;
434                     }
435                 } catch ( ConversionException e ) {
436                     continue;
437                 }
438 
439                 Resource r = getRestrictionValue( restriction );
440                 if ( r == null ) continue;
441 
442                 // if ( !( r instanceof OntClass ) ) {
443                 // // means our owl file is incomplete, is in tests.
444                 // log.info( r );
445                 // continue;
446                 // }
447 
448                 OntologyTerm child = fromOntClass( ( OntClass ) r );
449                 if ( !work.contains( child ) ) {
450                     work.add( child );
451                     if ( !direct ) ( ( OntologyTermImpl ) child ).getChildren( false, work );
452                 }
453 
454             }
455         }
456     }
457 
458     /**
459      * @param direct
460      * @param work
461      */
462     private void getParents( boolean direct, Collection<OntologyTerm> work ) {
463         assert work != null;
464         if ( !ontResource.isClass() ) {
465             return;
466         }
467 
468         ExtendedIterator<OntClass> iterator = ontResource.listSuperClasses( true );
469 
470         while ( iterator.hasNext() ) {
471 
472             try {
473                 OntClass c = iterator.next();
474 
475                 if ( USE_PROPER_PART_RESTRICTIONS && c.isRestriction() ) {
476                     Restriction restriction = c.asRestriction();
477 
478                     OntProperty onProperty = restriction.getOnProperty();
479 
480                     assert onProperty != null;
481 
482                     // We ignore this... hack.
483                     if ( !onProperty.getURI().equals( "http://www.obofoundry.org/ro/ro.owl#proper_part_of" ) ) {
484                         continue;
485                     }
486 
487                     Resource r = getRestrictionValue( restriction );
488 
489                     if ( r == null ) continue;
490 
491                     assert r != null;
492                     if ( log.isDebugEnabled() ) log.debug( " Some from:" + r + " " + onProperty.getURI() );
493 
494                     OntologyTerm parent = fromOntClass( ( OntClass ) r );
495 
496                     if ( REJECT_PARENT_URI.contains( parent.getUri() ) ) continue;
497 
498                     // avoid endless regression
499                     if ( !work.contains( parent ) ) {
500                         work.add( parent );
501                         if ( !direct ) ( ( OntologyTermImpl ) parent ).getParents( direct, work );
502                     }
503 
504                 } else {
505                     // not a restriction.
506                     OntologyTerm parent = this.fromOntClass( c );
507 
508                     if ( REJECT_PARENT_URI.contains( parent.getUri() ) ) continue;
509 
510                     if ( !work.contains( parent ) ) {
511                         work.add( parent );
512                         if ( !direct ) {
513                             // recurse.
514                             ( ( OntologyTermImpl ) parent ).getParents( direct, work );
515                         }
516                     }
517                 }
518             } catch ( ConversionException e ) {
519                 if ( log.isDebugEnabled() ) log.debug( e.getMessage() );
520                 continue;
521             }
522 
523         }
524     }
525 
526     private Resource getRestrictionValue( Restriction restriction ) {
527         Resource r = null;
528 
529         if ( restriction.isSomeValuesFromRestriction() ) {
530             SomeValuesFromRestriction some = restriction.asSomeValuesFromRestriction();
531             r = some.getSomeValuesFrom();
532         } else if ( restriction.isAllValuesFromRestriction() ) {
533             AllValuesFromRestriction allValues = restriction.asAllValuesFromRestriction();
534             r = allValues.getAllValuesFrom();
535         }
536         return r;
537     }
538 
539 }