source: TraumaOntologies/src/main/java/tudelft/healthpsychology/traumaontologies/owltree/OwlTreeReasoner.java@ 5

Last change on this file since 5 was 4, checked in by Bart Vastenhouw, 5 years ago

Added traumaontologies

File size: 9.0 KB
Line 
1package tudelft.healthpsychology.traumaontologies.owltree;
2
3import java.util.Arrays;
4import java.util.Collections;
5import java.util.LinkedList;
6import java.util.List;
7import java.util.Set;
8import java.util.stream.Collectors;
9
10import org.semanticweb.owlapi.model.AxiomType;
11import org.semanticweb.owlapi.model.IRI;
12import org.semanticweb.owlapi.model.OWLAnnotationAssertionAxiom;
13import org.semanticweb.owlapi.model.OWLClass;
14import org.semanticweb.owlapi.model.OWLDataProperty;
15import org.semanticweb.owlapi.model.OWLDataPropertyDomainAxiom;
16import org.semanticweb.owlapi.model.OWLDataPropertyRangeAxiom;
17import org.semanticweb.owlapi.model.OWLOntology;
18import org.semanticweb.owlapi.reasoner.InferenceType;
19import org.semanticweb.owlapi.reasoner.OWLReasoner;
20import org.semanticweb.owlapi.reasoner.OWLReasonerConfiguration;
21import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;
22import org.semanticweb.owlapi.reasoner.SimpleConfiguration;
23import org.semanticweb.owlapi.reasoner.structural.StructuralReasonerFactory;
24
25import uk.ac.manchester.cs.owl.owlapi.OWLDatatypeImpl;
26import uk.ac.manchester.cs.owl.owlapi.OWLLiteralImplString;
27
28/**
29 * OWLReasoner that assumes that the ontology contains a tree (not a graph)
30 * <ul>
31 * <li>each node has at most 1 parent
32 * <li>no cycles
33 * </ul>
34 * *
35 */
36public class OwlTreeReasoner {
37 private final OWLOntology ontology;
38 private final OWLReasoner reasoner;
39 public transient final OWLClass thing; // owl:Thing
40
41 public OwlTreeReasoner(OWLOntology ontology) {
42 this.ontology = ontology;
43 /*
44 * use reasoner, to do class hierarchy the inference stuff. There might
45 * be some tricky restrictions or other OWL tricks that we don't know of
46 * so better leave this to the official reasoners.
47 */
48 OWLReasonerFactory reasonerFactory = new StructuralReasonerFactory();
49 OWLReasonerConfiguration config = new SimpleConfiguration();
50 reasoner = reasonerFactory.createReasoner(ontology, config);
51 reasoner.precomputeInferences(InferenceType.CLASS_HIERARCHY);
52 // ignore if it's inconsistent. As long as we can get the superclasses,
53 // don't care.
54 thing = ontology.getOWLOntologyManager().getOWLDataFactory()
55 .getOWLThing();
56 }
57
58 /**
59 * @param obj the {@link OWLClass} to get the children of
60 * @return direct children of the given obj
61 *
62 */
63 public List<OWLClass> getChildren(OWLClass obj) {
64 return reasoner.getSubClasses(obj, true).entities()
65 .filter(o -> !o.isOWLNothing()).collect(Collectors.toList());
66 }
67
68 /**
69 * @param maxdepth Must be positive or 0. 0 means node itself, 1 means
70 * direct children, 2 means children of the children, etc.
71 * @return list with only this node if this is a leaf node or depth<=0. If
72 * this is not a leaf node, then returns a list joining all
73 * getChildren(depth-1) of each child. The difference with getLeaves
74 * is that this includes non-leave nodes at maxdepth.
75 */
76 public List<OWLClass> getChildren(OWLClass obj, int maxdepth) {
77 if (maxdepth <= 0) {
78 return Arrays.asList(obj);
79 }
80 List<OWLClass> children = reasoner.getSubClasses(obj, true).entities()
81 .filter(o -> !o.isOWLNothing()).collect(Collectors.toList());
82 if (children.isEmpty()) {
83 return Arrays.asList(obj);
84 }
85 return children.stream().map(child -> getChildren(child, maxdepth - 1))
86 .flatMap(List::stream).collect(Collectors.toList());
87 }
88
89 /**
90 * @param obj the {@link OWLClass} to get the leaf nodes from
91 * @param maxdepth the maximum depth of the leafs to be collected. 0 means
92 * obj itself.
93 * @return all leave nodes up do maxdepth deep. If given node is a leaf node
94 * (no children), returns list with only that node. returns empty
95 * list if maxdeptn<=0 and this node has children, or when the node
96 * has no laves at given depth.
97 *
98 */
99 public List<OWLClass> getLeaves(OWLClass obj, int maxdepth) {
100 // We have to remove "owl.NOTHING" objects, these are weird empty
101 // solution "objects".
102 List<OWLClass> children = reasoner.getSubClasses(obj, true).entities()
103 .filter(o -> !o.isOWLNothing()).collect(Collectors.toList());
104 if (children.isEmpty()) {
105 return Arrays.asList(obj);
106 }
107 // this is not a leaf node.
108 if (maxdepth <= 0) {
109 return Collections.emptyList();
110 }
111 return children.stream().map(child -> getLeaves(child, maxdepth - 1))
112 .flatMap(List::stream).collect(Collectors.toList());
113 }
114
115 /**
116 * @param obj the {@link OWLClass} to get the ancestors of
117 * @return all ancestors of the given obj, up to the root. Does not include
118 * the class itself.
119 *
120 */
121 public List<OWLClass> getAncestors(OWLClass obj) {
122 return new LinkedList<OWLClass>(
123 reasoner.getSuperClasses(obj, false).entities()
124 .filter(o -> !isThing(o)).collect(Collectors.toList()));
125 }
126
127 /**
128 *
129 * @param name the name for which an IRI is needed
130 * @return URI for the given object name in this ontology: the ontolyg
131 * basename followed by "#" and name.
132 */
133 public IRI getIri(String name) {
134 return IRI.create(getBaseName() + "#" + name);
135 }
136
137 public String getBaseName() {
138 return ontology.getOntologyID().getOntologyIRI().get().toString();
139 }
140
141 /**
142 *
143 * @param name the object name. See also {@link #getIri(String)}
144 * @return {@link OWLClass} with the given name
145 */
146 public OWLClass getObject(String name) {
147 IRI iri = getIri(name);
148 if (!ontology.containsClassInSignature(iri)) {
149 throw new IllegalArgumentException(
150 "Ontology does not contain object '#" + name + "'");
151 }
152 OWLClass obj = ontology.getOWLOntologyManager().getOWLDataFactory()
153 .getOWLClass(iri);
154 return obj;
155 }
156
157 /**
158 * @param obj the {@link OWLClass} to get the parent of
159 * @return direct parent of the given obj, or null if no parent
160 * @throws IllegalStateException if object has more than 1 parent.
161 *
162 */
163 public OWLClass getParent(OWLClass obj) {
164 Set<OWLClass> superClass = reasoner.getSuperClasses(obj, true)
165 .getFlattened();
166 if (superClass.size() > 1) {
167 throw new IllegalStateException(
168 "There is a bug in the ontology: class " + obj
169 + " has multiple parents: " + superClass);
170 }
171 if (superClass.isEmpty())
172 return null;
173 OWLClass s = superClass.iterator().next();
174 if (thing.equals(s)) // OWL adds "thing" at root. Remove it..
175 return null;
176 return s;
177 }
178
179 /**
180 * @param object
181 * @return all {@link OWLDataPropertyDomainAxiom}s of object and all
182 * ancestor classes. These axioms are basically URIs about which
183 * other info like domain, range and comments are stored.
184 */
185 public List<OWLDataPropertyDomainAxiom> getProperties(OWLClass object) {
186 List<OWLClass> thisAndAncestors = getAncestors(object);
187 thisAndAncestors.add(object);
188 List<OWLDataPropertyDomainAxiom> properties = new LinkedList<>();
189 for (OWLClass clas : thisAndAncestors) {
190 ontology.axioms(AxiomType.DATA_PROPERTY_DOMAIN)
191 .filter(dp -> clas.equals(dp.getDomain()))
192 .forEach(dp -> properties.add(dp));
193 }
194 return properties;
195 }
196
197 /**
198 *
199 * @param axiom an {@link OWLDataProperty}, typically an
200 * {@link OWLDataPropertyDomainAxiom}
201 * @return list of comment annotations for this axiom
202 */
203 public String getComment(OWLDataPropertyDomainAxiom axiom) {
204 List<String> list = axiom.dataPropertiesInSignature()
205 .map(odp -> getCommentAnnotation(odp))
206 .collect(Collectors.toList());
207 if (list.size() != 1) {
208 throw new IllegalStateException(
209 "There must be exactly 1 comment for " + axiom
210 + " but found " + list);
211 }
212 return list.get(0);
213 }
214
215 /**
216 * @param odp the {@link OWLDataPropertyDomainAxiom}
217 * @return the data ranges set for the given odp
218 */
219 public String getRange(OWLDataPropertyDomainAxiom odp) {
220 IRI property = (IRI) odp.getProperty().components().findFirst().get();
221
222 List<OWLDataPropertyRangeAxiom> axioms = ontology
223 .axioms(AxiomType.DATA_PROPERTY_RANGE)
224 .filter(p -> property
225 .equals(p.getProperty().components().findFirst().get()))
226 .collect(Collectors.toList());
227 if (axioms.size() != 1) {
228 throw new IllegalStateException(
229 "There are must be exactly 1 range axiom for " + odp
230 + " but found " + axioms);
231 }
232
233 // there is no doc on how to do this without nasty casts...
234 return ((OWLDatatypeImpl) axioms.get(0).getRange()).getIRI().toString();
235 }
236
237 /**
238 *
239 * @param c an {@link OWLClass} object
240 * @return true iff c equals to Owl:Thing.
241 */
242 public boolean isThing(OWLClass c) {
243 return thing.equals(c);
244 }
245
246 /**
247 * @param odp
248 * @return the annotation for this OWLDataProperty.
249 */
250 private String getCommentAnnotation(OWLDataProperty odp) {
251 List<OWLAnnotationAssertionAxiom> annotationaxioms = ontology
252 .annotationAssertionAxioms(odp.getIRI())
253 .collect(Collectors.toList());
254 if (annotationaxioms.size() != 1) {
255 // we don't allow because we can't figure out the range in that
256 // case.
257 throw new IllegalStateException(
258 "There must be exactly 1 comment for " + odp
259 + " but found " + annotationaxioms);
260 }
261 // there is no doc on how to do this without nasty casts...
262 return ((OWLLiteralImplString) annotationaxioms.get(0).getAnnotation()
263 .annotationValue()).getLiteral();
264
265 }
266
267}
Note: See TracBrowser for help on using the repository browser.