Almost every OWL API misunderstanding traces back to one of five ideas. Get these straight and the API stops feeling arbitrary.
Not a graph you navigate, not an object with setters. OWLOntology is,
conceptually, a Set<OWLAxiom> with an identity.
Consequences that surprise people:
- There is no
pizza.setSuperClass(food). You add aSubClassOf(Pizza, Food)axiom. - Axiom sets are unordered. If you need stable output, sort it yourself.
- Two ontologies with the same axioms are logically identical regardless of which file format they came from.
An OWLClass is an IRI plus a Java type. That is all. It holds no list of
superclasses, no properties, no annotations.
OWLClass pizza = factory.getOWLClass(IRI.create(NS + "Pizza"));
// Nothing has been added to any ontology. This is just a name.So "what are Pizza's superclasses?" is not a question you ask the class. You ask an ontology (what is asserted) or a reasoner (what follows).
This is also why OWLEntityRenamer exists: since an entity is its IRI,
renaming means rewriting every axiom that mentions the old IRI.
You never new an OWL API model object. OWLDataFactory is the single
constructor for entities, class expressions, axioms and literals. That
indirection lets the implementation intern objects and keep them immutable —
which is why entities are safe to use as HashMap keys and to share freely
across threads.
OWLOntologyManager creates, loads and saves ontologies, resolves imports, and
applies changes. Two rules follow:
- One manager cannot hold two ontologies with the same ontology IRI. Trying
throws
OWLOntologyAlreadyExistsException. Use a fresh manager to reload something you already have in memory. - Changes go through the manager, as
OWLOntologyChangeobjects. This is what makes undo, batching and change listeners possible.
manager.addAxiom(o, ax) is sugar for
manager.applyChange(new AddAxiom(o, ax)).
The single most important distinction in OWL.
| Question | Ask |
|---|---|
| What did someone write down? | the OWLOntology |
| What logically follows? | an OWLReasoner |
ontology.containsAxiom(f.getOWLSubClassOfAxiom(margherita, vegetarianPizza));
// false -- nobody asserted this
reasoner.isEntailed(f.getOWLSubClassOfAxiom(margherita, vegetarianPizza));
// true -- but it follows from the definitionsIn Protégé this is the white (asserted) versus yellow (inferred) distinction in the class hierarchy.
Two corollaries that bite:
Open world. Absence of information is not negation. If nothing says a pizza
has a meat topping, OWL does not conclude it has none. It concludes nothing.
That is why hasTopping only CheeseTopping is satisfied by a pizza with no
toppings at all.
No unique names. Two differently-named individuals may be the same thing
unless you say otherwise (DifferentIndividuals, or HasKey).
OWLOntologyManager ── creates/loads/saves ──► OWLOntology
│ (Set<OWLAxiom>)
│ getOWLDataFactory() ▲
▼ │ axioms built by
OWLDataFactory ──── builds entities, expressions, axioms
OWLReasonerFactory ── createReasoner(ontology) ──► OWLReasoner
(answers entailment
questions; needs flush()
after edits)
Nearly every query method has an overload taking
org.semanticweb.owlapi.model.parameters.Imports:
Imports.EXCLUDED— only this ontology's own axioms. This is the no-arg default.Imports.INCLUDED— this ontology plus its whole import closure.
If a query mysteriously cannot find an axiom you know exists, this flag is the first thing to check. And remember: an axiom belongs to exactly one ontology. Adding to an importing ontology never modifies the imported one — in Protégé, that is what the "active ontology" selector controls.
This tutorial targets 4.x because that is what Protégé 5.x ships. The two differences you will actually trip over:
| 4.x (here) | 5.x | |
|---|---|---|
| Optional type | com.google.common.base.Optional (Guava) |
java.util.Optional |
| Collections | Set<...> returns |
streams (...Stream() methods) |
So in 4.x you write ontology.getOntologyID().getOntologyIRI().orNull(), where
5.x would use .orElse(null). Code samples found online frequently mix these
up; if a snippet does not compile, the version is the first suspect.