Skip to content

Commit a825e3e

Browse files
committed
Merge branch 'hotfix-1.1.19'
2 parents 2118b18 + 8ca9382 commit a825e3e

12 files changed

Lines changed: 347 additions & 52 deletions

pom.xml

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<name>baseCode</name>
66
<groupId>baseCode</groupId>
77
<artifactId>baseCode</artifactId>
8-
<version>1.1.18</version>
8+
<version>1.1.19</version>
99
<inceptionYear>2003</inceptionYear>
1010
<description>
1111
<![CDATA[Data structures, math and statistics tools, and utilities that are often needed across projects.]]>
@@ -76,12 +76,12 @@
7676
<dependency>
7777
<groupId>org.apache.commons</groupId>
7878
<artifactId>commons-lang3</artifactId>
79-
<version>3.12.0</version>
79+
<version>3.13.0</version>
8080
</dependency>
8181
<dependency>
8282
<groupId>commons-io</groupId>
8383
<artifactId>commons-io</artifactId>
84-
<version>2.11.0</version>
84+
<version>2.13.0</version>
8585
</dependency>
8686
<dependency>
8787
<groupId>org.apache.commons</groupId>
@@ -228,7 +228,13 @@
228228
<dependency>
229229
<groupId>com.opencsv</groupId>
230230
<artifactId>opencsv</artifactId>
231-
<version>5.7.1</version>
231+
<version>5.8</version>
232+
<exclusions>
233+
<exclusion>
234+
<groupId>commons-beanutils</groupId>
235+
<artifactId>commons-beanutils</artifactId>
236+
</exclusion>
237+
</exclusions>
232238
</dependency>
233239

234240
<!-- Development tools -->
@@ -239,6 +245,13 @@
239245
</dependency>
240246

241247
<!-- Testing utilities -->
248+
<!-- FIXME: remove this dependency altogether, but it's used indirectly in ConfigUtilsTest -->
249+
<dependency>
250+
<groupId>commons-beanutils</groupId>
251+
<artifactId>commons-beanutils</artifactId>
252+
<version>1.9.4</version>
253+
<scope>test</scope>
254+
</dependency>
242255
<dependency>
243256
<groupId>junit</groupId>
244257
<artifactId>junit</artifactId>

src/ubic/basecode/ontology/jena/AbstractOntologyMemoryBackedService.java

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515
package ubic.basecode.ontology.jena;
1616

1717
import com.hp.hpl.jena.ontology.OntModelSpec;
18+
import com.hp.hpl.jena.ontology.ProfileRegistry;
19+
import com.hp.hpl.jena.rdf.model.ModelFactory;
20+
import com.hp.hpl.jena.reasoner.ReasonerFactory;
21+
import com.hp.hpl.jena.reasoner.rulesys.OWLFBRuleReasonerFactory;
22+
import com.hp.hpl.jena.reasoner.rulesys.OWLMicroReasonerFactory;
23+
import com.hp.hpl.jena.reasoner.rulesys.OWLMiniReasonerFactory;
24+
import com.hp.hpl.jena.reasoner.transitiveReasoner.TransitiveReasonerFactory;
1825
import ubic.basecode.ontology.model.OntologyModel;
1926
import ubic.basecode.util.Configuration;
2027

@@ -35,23 +42,50 @@ protected String getOntologyUrl() {
3542
}
3643

3744
@Override
38-
protected OntologyModel loadModel( boolean processImports, InferenceMode inferenceMode ) throws IOException {
39-
return new OntologyModelImpl( OntologyLoader.loadMemoryModel( this.getOntologyUrl(), this.getCacheName(), processImports, this.getSpec( inferenceMode ) ) );
45+
protected OntologyModel loadModel( boolean processImports, LanguageLevel languageLevel, InferenceMode inferenceMode ) throws IOException {
46+
return new OntologyModelImpl( OntologyLoader.loadMemoryModel( this.getOntologyUrl(), this.getCacheName(), processImports, this.getSpec( languageLevel, inferenceMode ) ) );
4047
}
4148

4249
@Override
43-
protected OntologyModel loadModelFromStream( InputStream is, boolean processImports, InferenceMode inferenceMode ) throws IOException {
44-
return new OntologyModelImpl( OntologyLoader.loadMemoryModel( is, this.getOntologyUrl(), processImports, this.getSpec( inferenceMode ) ) );
50+
protected OntologyModel loadModelFromStream( InputStream is, boolean processImports, LanguageLevel languageLevel, InferenceMode inferenceMode ) throws IOException {
51+
return new OntologyModelImpl( OntologyLoader.loadMemoryModel( is, this.getOntologyUrl(), processImports, this.getSpec( languageLevel, inferenceMode ) ) );
4552
}
4653

47-
private OntModelSpec getSpec( InferenceMode inferenceMode ) {
54+
private OntModelSpec getSpec( LanguageLevel languageLevel, InferenceMode inferenceMode ) {
55+
String profile;
56+
switch ( languageLevel ) {
57+
case FULL:
58+
profile = ProfileRegistry.OWL_LANG;
59+
break;
60+
case DL:
61+
profile = ProfileRegistry.OWL_DL_LANG;
62+
break;
63+
case LITE:
64+
profile = ProfileRegistry.OWL_LITE_LANG;
65+
break;
66+
default:
67+
throw new UnsupportedOperationException( String.format( "Unsupported OWL language level %s.", languageLevel ) );
68+
}
69+
ReasonerFactory reasonerFactory;
4870
switch ( inferenceMode ) {
71+
case FULL:
72+
reasonerFactory = OWLFBRuleReasonerFactory.theInstance();
73+
break;
74+
case MINI:
75+
reasonerFactory = OWLMiniReasonerFactory.theInstance();
76+
break;
77+
case MICRO:
78+
reasonerFactory = OWLMicroReasonerFactory.theInstance();
79+
break;
4980
case TRANSITIVE:
50-
return OntModelSpec.OWL_MEM_TRANS_INF;
81+
reasonerFactory = TransitiveReasonerFactory.theInstance();
82+
break;
5183
case NONE:
52-
return OntModelSpec.OWL_MEM;
84+
reasonerFactory = null;
85+
break;
5386
default:
5487
throw new UnsupportedOperationException( String.format( "Unsupported inference level %s.", inferenceMode ) );
5588
}
89+
return new OntModelSpec( ModelFactory.createMemModelMaker(), null, reasonerFactory, profile );
5690
}
5791
}

src/ubic/basecode/ontology/jena/AbstractOntologyService.java

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.hp.hpl.jena.rdf.arp.ParseException;
2525
import com.hp.hpl.jena.rdf.model.Property;
2626
import com.hp.hpl.jena.rdf.model.Resource;
27+
import com.hp.hpl.jena.rdf.model.ResourceFactory;
2728
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
2829
import org.apache.commons.lang3.RandomStringUtils;
2930
import org.apache.commons.lang3.StringUtils;
@@ -65,18 +66,20 @@ public abstract class AbstractOntologyService implements OntologyService {
6566
/**
6667
* Properties through which propagation is allowed for {@link #getParents(Collection, boolean, boolean)}}
6768
*/
68-
private static final Set<Property> additionalProperties;
69+
private static final Set<String> DEFAULT_ADDITIONAL_PROPERTIES;
6970

7071
static {
71-
additionalProperties = new HashSet<>();
72-
additionalProperties.add( BFO.partOf );
73-
additionalProperties.add( RO.properPartOf );
72+
DEFAULT_ADDITIONAL_PROPERTIES = new HashSet<>();
73+
DEFAULT_ADDITIONAL_PROPERTIES.add( BFO.partOf.getURI() );
74+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.properPartOf.getURI() );
7475
}
7576

7677
/* settings (applicable for next initialization) */
78+
private LanguageLevel nextLanguageLevel = LanguageLevel.FULL;
7779
private InferenceMode nextInferenceMode = InferenceMode.TRANSITIVE;
7880
private boolean nextProcessImports = true;
7981
private boolean nextSearchEnabled = true;
82+
private Set<String> nextAdditionalPropertyUris = DEFAULT_ADDITIONAL_PROPERTIES;
8083

8184
/**
8285
* Lock used to prevent reads while the ontology is being initialized.
@@ -91,11 +94,31 @@ public abstract class AbstractOntologyService implements OntologyService {
9194
private Set<Restriction> additionalRestrictions;
9295
private boolean isInitialized = false;
9396
@Nullable
97+
private LanguageLevel languageLevel = null;
98+
@Nullable
9499
private InferenceMode inferenceMode = null;
95100
@Nullable
96101
private Boolean processImports = null;
97102
@Nullable
98103
private Boolean searchEnabled = null;
104+
@Nullable
105+
private Set<String> additionalPropertyUris = null;
106+
107+
@Override
108+
public LanguageLevel getLanguageLevel() {
109+
Lock lock = rwLock.readLock();
110+
try {
111+
lock.lock();
112+
return this.languageLevel != null ? this.languageLevel : nextLanguageLevel;
113+
} finally {
114+
lock.unlock();
115+
}
116+
}
117+
118+
@Override
119+
public void setLanguageLevel( LanguageLevel languageLevel ) {
120+
this.nextLanguageLevel = languageLevel;
121+
}
99122

100123
@Override
101124
public InferenceMode getInferenceMode() {
@@ -145,6 +168,22 @@ public void setSearchEnabled( boolean searchEnabled ) {
145168
this.nextSearchEnabled = searchEnabled;
146169
}
147170

171+
@Override
172+
public Set<String> getAdditionalPropertyUris() {
173+
Lock lock = rwLock.readLock();
174+
try {
175+
lock.lock();
176+
return additionalPropertyUris != null ? additionalPropertyUris : nextAdditionalPropertyUris;
177+
} finally {
178+
lock.unlock();
179+
}
180+
}
181+
182+
@Override
183+
public void setAdditionalPropertyUris( Set<String> additionalPropertyUris ) {
184+
this.nextAdditionalPropertyUris = additionalPropertyUris;
185+
}
186+
148187
public void initialize( boolean forceLoad, boolean forceIndexing ) {
149188
initialize( null, forceLoad, forceIndexing );
150189
}
@@ -159,9 +198,13 @@ private void initialize( @Nullable InputStream stream, boolean forceLoad, boolea
159198
return;
160199
}
161200

201+
// making a copy of all we need
162202
String ontologyUrl = getOntologyUrl();
163203
String ontologyName = getOntologyName();
164204
String cacheName = getCacheName();
205+
Set<Property> additionalProperties = nextAdditionalPropertyUris.stream()
206+
.map( ResourceFactory::createProperty ).collect( Collectors.toSet() );
207+
LanguageLevel languageLevel = nextLanguageLevel;
165208
InferenceMode inferenceMode = nextInferenceMode;
166209
boolean processImports = nextProcessImports;
167210
boolean searchEnabled = nextSearchEnabled;
@@ -196,7 +239,7 @@ private void initialize( @Nullable InputStream stream, boolean forceLoad, boolea
196239
return;
197240

198241
try {
199-
OntologyModel m = stream != null ? loadModelFromStream( stream, processImports, inferenceMode ) : loadModel( processImports, inferenceMode ); // can take a while.
242+
OntologyModel m = stream != null ? loadModelFromStream( stream, processImports, languageLevel, inferenceMode ) : loadModel( processImports, languageLevel, inferenceMode ); // can take a while.
200243
if ( m instanceof OntologyModelImpl ) {
201244
model = ( ( OntologyModelImpl ) m ).getOntModel();
202245
} else {
@@ -254,9 +297,11 @@ private void initialize( @Nullable InputStream stream, boolean forceLoad, boolea
254297
this.additionalRestrictions = additionalRestrictions;
255298
this.index = index;
256299
this.isInitialized = true;
300+
this.languageLevel = languageLevel;
257301
this.inferenceMode = inferenceMode;
258302
this.processImports = processImports;
259303
this.searchEnabled = searchEnabled;
304+
this.additionalPropertyUris = additionalProperties.stream().map( Property::getURI ).collect( Collectors.toSet() );
260305
if ( cacheName != null ) {
261306
// now that the terms have been replaced, we can clear old caches
262307
try {
@@ -615,13 +660,13 @@ public void waitForInitializationThread() throws InterruptedException {
615660
* Delegates the call as to load the model into memory or leave it on disk. Simply delegates to either
616661
* OntologyLoader.loadMemoryModel( url ); OR OntologyLoader.loadPersistentModel( url, spec );
617662
*/
618-
protected abstract OntologyModel loadModel( boolean processImports, InferenceMode inferenceMode ) throws IOException;
663+
protected abstract OntologyModel loadModel( boolean processImports, LanguageLevel languageLevel, InferenceMode inferenceMode ) throws IOException;
619664

620665

621666
/**
622667
* Load a model from a given input stream.
623668
*/
624-
protected abstract OntologyModel loadModelFromStream( InputStream stream, boolean processImports, InferenceMode inferenceMode ) throws IOException;
669+
protected abstract OntologyModel loadModelFromStream( InputStream stream, boolean processImports, LanguageLevel languageLevel, InferenceMode inferenceMode ) throws IOException;
625670

626671
/**
627672
* A name for caching this ontology, or null to disable caching.
@@ -633,17 +678,6 @@ protected String getCacheName() {
633678
return getOntologyName();
634679
}
635680

636-
private OntModelSpec getSpec( InferenceMode inferenceMode ) {
637-
switch ( inferenceMode ) {
638-
case TRANSITIVE:
639-
return OntModelSpec.OWL_MEM_TRANS_INF;
640-
case NONE:
641-
return OntModelSpec.OWL_MEM;
642-
default:
643-
throw new UnsupportedOperationException( String.format( "Unsupported inference level %s.", inferenceMode ) );
644-
}
645-
}
646-
647681
@Override
648682
public void index( boolean force ) {
649683
String cacheName = getCacheName();
@@ -743,7 +777,8 @@ public void loadTermsInNameSpace( InputStream is, boolean forceIndex ) {
743777

744778
@Override
745779
public String toString() {
746-
return String.format( "%s [%s]", getOntologyName(), getOntologyUrl() );
780+
return String.format( "%s [url=%s] [language level=%s] [inference mode=%s] [imports=%b] [search=%b]",
781+
getOntologyName(), getOntologyUrl(), getLanguageLevel(), getInferenceMode(), getProcessImports(), isSearchEnabled() );
747782
}
748783

749784
private Set<OntClass> getOntClassesFromTerms( Collection<OntologyTerm> terms ) {

src/ubic/basecode/ontology/jena/JenaUtils.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import com.hp.hpl.jena.util.iterator.Filter;
99
import com.hp.hpl.jena.util.iterator.UniqueExtendedIterator;
1010
import org.apache.commons.lang3.time.StopWatch;
11+
import org.slf4j.Logger;
12+
import org.slf4j.LoggerFactory;
1113

1214
import javax.annotation.Nullable;
1315
import java.util.*;
@@ -18,7 +20,25 @@
1820

1921
class JenaUtils {
2022

23+
protected static Logger log = LoggerFactory.getLogger( JenaUtils.class );
24+
2125
public static Collection<OntClass> getParents( OntModel model, Collection<OntClass> ontClasses, boolean direct, @Nullable Set<Restriction> additionalRestrictions ) {
26+
Collection<OntClass> parents = getParentsInternal( model, ontClasses, direct, additionalRestrictions );
27+
if ( shouldRevisit( parents, direct, model, additionalRestrictions ) ) {
28+
// if there are some missing direct parents, revisit the hierarchy
29+
Set<OntClass> parentsToRevisit = new HashSet<>( parents );
30+
while ( !parentsToRevisit.isEmpty() ) {
31+
log.debug( "Revisiting the direct parents of {} terms...", parentsToRevisit.size() );
32+
parentsToRevisit = new HashSet<>( getParentsInternal( model, parentsToRevisit, true, additionalRestrictions ) );
33+
parentsToRevisit.removeAll( parents );
34+
log.debug( "Found {} missed parents.", parentsToRevisit.size() );
35+
parents.addAll( parentsToRevisit );
36+
}
37+
}
38+
return parents;
39+
}
40+
41+
private static Collection<OntClass> getParentsInternal( OntModel model, Collection<OntClass> ontClasses, boolean direct, @Nullable Set<Restriction> additionalRestrictions ) {
2242
ontClasses = ontClasses.stream()
2343
.map( t -> t.inModel( model ) )
2444
.filter( t -> t.canAs( OntClass.class ) )
@@ -65,6 +85,22 @@ public static Collection<OntClass> getParents( OntModel model, Collection<OntCla
6585
}
6686

6787
public static Collection<OntClass> getChildren( OntModel model, Collection<OntClass> terms, boolean direct, @Nullable Set<Restriction> additionalRestrictions ) {
88+
Collection<OntClass> children = getChildrenInternal( model, terms, direct, additionalRestrictions );
89+
if ( shouldRevisit( children, direct, model, additionalRestrictions ) ) {
90+
// if there are some missing direct children, revisit the hierarchy
91+
Set<OntClass> childrenToRevisit = new HashSet<>( children );
92+
while ( !childrenToRevisit.isEmpty() ) {
93+
log.debug( "Revisiting the direct parents of {} terms...", childrenToRevisit.size() );
94+
childrenToRevisit = new HashSet<>( JenaUtils.getChildrenInternal( model, childrenToRevisit, true, additionalRestrictions ) );
95+
childrenToRevisit.removeAll( children );
96+
log.debug( "Found {} missed children.", childrenToRevisit.size() );
97+
children.addAll( childrenToRevisit );
98+
}
99+
}
100+
return children;
101+
}
102+
103+
public static Collection<OntClass> getChildrenInternal( OntModel model, Collection<OntClass> terms, boolean direct, @Nullable Set<Restriction> additionalRestrictions ) {
68104
terms = terms.stream()
69105
.map( t -> t.inModel( model ) )
70106
.filter( t -> t.canAs( OntClass.class ) )
@@ -103,6 +139,43 @@ public static Collection<OntClass> getChildren( OntModel model, Collection<OntCl
103139
return result;
104140
}
105141

142+
/**
143+
* Check if a set of terms should be revisited to find missing parents or children.
144+
* <p>
145+
* To be considered, the model must have a reasoner that lacks one of {@code rdfs:subClassOf}, {@code owl:subValuesFrom}
146+
* or {@code owl:allValuesFrom} inference capabilities. If a model has no reasoner, revisiting is not desirable and
147+
* thus this will return false.
148+
* <p>
149+
* If direct is false or terms is empty, it's not worth revisiting.
150+
*/
151+
private static boolean shouldRevisit( Collection<OntClass> terms, boolean direct, OntModel model, @Nullable Set<Restriction> additionalRestrictions ) {
152+
return !direct
153+
&& !terms.isEmpty()
154+
&& additionalRestrictions != null
155+
&& !additionalRestrictions.isEmpty()
156+
&& model.getReasoner() != null
157+
&& ( !supportsSubClassInference( model ) || !supportsAdditionalRestrictionsInference( model ) );
158+
}
159+
160+
/**
161+
* Check if an ontology model supports inference of {@code rdfs:subClassOf}.
162+
*/
163+
public static boolean supportsSubClassInference( OntModel model ) {
164+
return model.getReasoner() != null
165+
&& model.getReasoner().supportsProperty( model.getProfile().SUB_CLASS_OF() );
166+
}
167+
168+
/**
169+
* Checks if an ontology model supports inference of with additional restrictions.
170+
* <p>
171+
* This covers {@code owl:subValuesFrom} and {@code owl:allValuesFrom} restrictions.
172+
*/
173+
public static boolean supportsAdditionalRestrictionsInference( OntModel model ) {
174+
return model.getReasoner() != null
175+
&& model.getReasoner().supportsProperty( model.getProfile().SOME_VALUES_FROM() )
176+
&& model.getReasoner().supportsProperty( model.getProfile().ALL_VALUES_FROM() );
177+
}
178+
106179
public static Resource getRestrictionValue( Restriction r ) {
107180
if ( r.isSomeValuesFromRestriction() ) {
108181
return r.asSomeValuesFromRestriction().getSomeValuesFrom();

0 commit comments

Comments
 (0)