1 package ubic.basecode.ontology.jena;
2
3 import com.hp.hpl.jena.ontology.ConversionException;
4 import com.hp.hpl.jena.ontology.OntClass;
5 import com.hp.hpl.jena.ontology.OntModel;
6 import com.hp.hpl.jena.ontology.Restriction;
7 import com.hp.hpl.jena.rdf.model.*;
8 import com.hp.hpl.jena.util.iterator.ExtendedIterator;
9 import com.hp.hpl.jena.util.iterator.Filter;
10 import com.hp.hpl.jena.util.iterator.UniqueExtendedIterator;
11 import org.apache.commons.lang3.time.StopWatch;
12 import org.slf4j.Logger;
13 import org.slf4j.LoggerFactory;
14
15 import javax.annotation.Nullable;
16 import java.util.*;
17 import java.util.function.Predicate;
18 import java.util.stream.Collectors;
19
20 import static com.hp.hpl.jena.reasoner.ReasonerRegistry.makeDirect;
21
22 class JenaUtils {
23
24 protected static Logger log = LoggerFactory.getLogger( JenaUtils.class );
25
26 public static Collection<OntClass> getParents( OntModel model, Collection<OntClass> ontClasses, boolean direct, @Nullable Set<Restriction> additionalRestrictions ) {
27 Collection<OntClass> parents = getParentsInternal( model, ontClasses, direct, additionalRestrictions );
28 if ( shouldRevisit( parents, direct, model, additionalRestrictions ) ) {
29
30 Set<OntClass> parentsToRevisit = new HashSet<>( parents );
31 while ( !parentsToRevisit.isEmpty() ) {
32 log.debug( "Revisiting the direct parents of {} terms...", parentsToRevisit.size() );
33 parentsToRevisit = new HashSet<>( getParentsInternal( model, parentsToRevisit, true, additionalRestrictions ) );
34 parentsToRevisit.removeAll( parents );
35 log.debug( "Found {} missed parents.", parentsToRevisit.size() );
36 parents.addAll( parentsToRevisit );
37 }
38 }
39 return parents;
40 }
41
42 private static Collection<OntClass> getParentsInternal( OntModel model, Collection<OntClass> ontClasses, boolean direct, @Nullable Set<Restriction> additionalRestrictions ) {
43 ontClasses = ontClasses.stream()
44 .map( t -> t.inModel( model ) )
45 .filter( t -> t.canAs( OntClass.class ) )
46 .map( t -> {
47 try {
48 return t.as( OntClass.class );
49 } catch ( ConversionException e ) {
50 log.error( "Conversion failed for " + t, e );
51 return null;
52 }
53 } )
54 .filter( Objects::nonNull )
55 .collect( Collectors.toList() );
56 if ( ontClasses.isEmpty() ) {
57 return Collections.emptySet();
58 }
59 Iterator<OntClass> it = ontClasses.iterator();
60 ExtendedIterator<OntClass> iterator = it.next().listSuperClasses( direct );
61 while ( it.hasNext() ) {
62 iterator = iterator.andThen( it.next().listSuperClasses( direct ) );
63 }
64
65 Collection<OntClass> result = new HashSet<>();
66 while ( iterator.hasNext() ) {
67 OntClass c = iterator.next();
68
69
70 if ( additionalRestrictions != null && !additionalRestrictions.isEmpty() && c.isRestriction() ) {
71 Restriction r = c.asRestriction();
72 if ( additionalRestrictions.contains( r ) ) {
73 Resource value = getRestrictionValue( r );
74 if ( value instanceof OntClass ) {
75 c = ( OntClass ) value;
76 } else {
77 continue;
78 }
79 }
80 }
81
82
83 if ( c.getURI() == null )
84 continue;
85
86
87 if ( c.equals( model.getProfile().THING() ) )
88 continue;
89
90 result.add( c );
91 }
92
93 return result;
94 }
95
96 public static Collection<OntClass> getChildren( OntModel model, Collection<OntClass> terms, boolean direct, @Nullable Set<Restriction> additionalRestrictions ) {
97 Collection<OntClass> children = getChildrenInternal( model, terms, direct, additionalRestrictions );
98 if ( shouldRevisit( children, direct, model, additionalRestrictions ) ) {
99
100 Set<OntClass> childrenToRevisit = new HashSet<>( children );
101 while ( !childrenToRevisit.isEmpty() ) {
102 log.debug( "Revisiting the direct parents of {} terms...", childrenToRevisit.size() );
103 childrenToRevisit = new HashSet<>( JenaUtils.getChildrenInternal( model, childrenToRevisit, true, additionalRestrictions ) );
104 childrenToRevisit.removeAll( children );
105 log.debug( "Found {} missed children.", childrenToRevisit.size() );
106 children.addAll( childrenToRevisit );
107 }
108 }
109 return children;
110 }
111
112 public static Collection<OntClass> getChildrenInternal( OntModel model, Collection<OntClass> terms, boolean direct, @Nullable Set<Restriction> additionalRestrictions ) {
113 terms = terms.stream()
114 .map( t -> t.inModel( model ) )
115 .filter( t -> t.canAs( OntClass.class ) )
116 .map( t -> {
117 try {
118 return t.as( OntClass.class );
119 } catch ( ConversionException e ) {
120 log.error( "Conversion failed for " + t, e );
121 return null;
122 }
123 } )
124 .filter( Objects::nonNull )
125 .collect( Collectors.toList() );
126 if ( terms.isEmpty() ) {
127 return Collections.emptySet();
128 }
129 StopWatch timer = StopWatch.createStarted();
130 Iterator<OntClass> it = terms.iterator();
131 ExtendedIterator<OntClass> iterator = it.next().listSubClasses( direct );
132 while ( it.hasNext() ) {
133 iterator = iterator.andThen( it.next().listSubClasses( direct ) );
134 }
135 Set<OntClass> result = iterator
136 .filterDrop( new BnodeFilter<>() )
137 .filterDrop( new PredicateFilter<>( o -> o.equals( model.getProfile().NOTHING() ) ) )
138 .toSet();
139 if ( additionalRestrictions != null && !additionalRestrictions.isEmpty() ) {
140 timer.reset();
141 timer.start();
142 Property subClassOf = model.getProfile().SUB_CLASS_OF();
143 if ( direct ) {
144 subClassOf = ResourceFactory.createProperty( makeDirect( subClassOf.getURI() ) );
145 }
146 Set<Restriction> restrictions = UniqueExtendedIterator.create( additionalRestrictions.iterator() )
147 .filterKeep( new RestrictionWithValuesFromFilter( terms ) )
148 .toSet();
149 for ( Restriction r : restrictions ) {
150 result.addAll( model.listResourcesWithProperty( subClassOf, r )
151 .filterDrop( new BnodeFilter<>() )
152 .mapWith( r2 -> r2.as( OntClass.class ) )
153 .toSet() );
154 }
155 }
156 return result;
157 }
158
159
160
161
162
163
164
165
166
167
168 private static boolean shouldRevisit( Collection<OntClass> terms, boolean direct, OntModel model, @Nullable Set<Restriction> additionalRestrictions ) {
169 return !direct
170 && !terms.isEmpty()
171 && additionalRestrictions != null
172 && !additionalRestrictions.isEmpty()
173 && model.getReasoner() != null
174 && ( !supportsSubClassInference( model ) || !supportsAdditionalRestrictionsInference( model ) );
175 }
176
177
178
179
180 public static boolean supportsSubClassInference( OntModel model ) {
181 return model.getReasoner() != null
182 && model.getReasoner().supportsProperty( model.getProfile().SUB_CLASS_OF() );
183 }
184
185
186
187
188
189
190 public static boolean supportsAdditionalRestrictionsInference( OntModel model ) {
191 return model.getReasoner() != null
192 && model.getReasoner().supportsProperty( model.getProfile().SOME_VALUES_FROM() )
193 && model.getReasoner().supportsProperty( model.getProfile().ALL_VALUES_FROM() );
194 }
195
196 public static Resource getRestrictionValue( Restriction r ) {
197 if ( r.isSomeValuesFromRestriction() ) {
198 return r.asSomeValuesFromRestriction().getSomeValuesFrom();
199 } else if ( r.isAllValuesFromRestriction() ) {
200 return r.asAllValuesFromRestriction().getAllValuesFrom();
201 } else {
202 return null;
203 }
204 }
205
206
207
208
209
210 public static String asString( RDFNode object ) {
211 return ( String ) object.visitWith( new RDFVisitor() {
212
213 @Override
214 public Object visitBlank( Resource r, AnonId id ) {
215 return r.getLocalName();
216 }
217
218 @Override
219 public Object visitLiteral( Literal l ) {
220 return l.toString().replaceAll( "\\^\\^.+", "" );
221 }
222
223 @Override
224 public Object visitURI( Resource r, String uri ) {
225 return r.getLocalName();
226 }
227 } );
228 }
229
230 public static <T> Filter<T> where( Predicate<T> predicate ) {
231 return new PredicateFilter<>( predicate );
232 }
233 }