Repository: ontodev/robot
Pull request #799 (2021-01-21 18:52:07)

Remove validate command
- [x] `docs/` have been added/updated
- [x] tests have been added/updated
- [x] `mvn verify` says all tests pass
- [ ] `mvn site` says all JavaDocs correct
- [ ] `CHANGELOG.md` has been updated

We now have a [tag](https://github.com/ontodev/robot/releases/tag/validate-command) to find the `validate` command. Removing all `validate`-related files so that this command does not get released.

Looks like `ExplainOperation` also got reformatted.

Not sure what we want to say in the CHANGELOG? That `validate` was removed? Or should we remove any references to `validate`?


Patch:
diff --git a/docs/examples/immune_exposures.csv b/docs/examples/immune_exposures.csv
deleted file mode 100644
index c887b09e..00000000
--- a/docs/examples/immune_exposures.csv
+++ /dev/null
@@ -1,13 +0,0 @@
-exposure process reported,exposure material reported,exposure material id,disease reported,disease ontology id,disease stage reported
-unknown,,,,,
-administering substance in vivo,Hepacivirus C,NCBITaxon:11103,,,
-exposure to substance without evidence for disease,Dengue virus,NCBITaxon:12637,,,
-occurrence of disease,,,,,
-occurrence of infectious disease,Dengue virus,NCBITaxon:12637,dengue hemorrhagic fever,DOID:12206,Acute/Recent onset
-occurrence of infectious disease,Dengue virus,NCBITaxon:12637,,,Acute/Recent onset
-occurrence of infectious disease,Dengue virus,NCBITaxon:12637,dengue hemorrhagic fever,DOID:12206,
-occurrence of infectious disease,Dengue virus,NCBITaxon:11060,dengue hemorrhagic fever,DOID:12206,Acute/Recent onset
-occurrence of infectious disease,Dengue virus,NCBITaxon:12637,Dengue virus 2,NCBITaxon:11060,Acute/Recent onset
-('administering substance in vivo' or 'exposure to substance without evidence for disease'),blood or 'Hepacivirus C',UBERON:0000178,,,Chronic
-occurrence of infectious disease,Dengue virus,NCBITaxon:12637,dengue hemorrhagic fever|wheat allergy,DOID:12206|DOID:3660,Acute/Recent onset
-occurrence of infectious disease,Dengue virus,NCBITaxon:12637,dengue hemorrhagic fever|dengue hemorrhagic fever,DOID:12206|DOID:12206,Acute/Recent onset
\ No newline at end of file
diff --git a/docs/examples/immune_exposures.html b/docs/examples/immune_exposures.html
deleted file mode 100644
index 6c1f6ac9..00000000
--- a/docs/examples/immune_exposures.html
+++ /dev/null
@@ -1,120 +0,0 @@
-<head>
-	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
-	<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
-	<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
-	<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
-</head>
-<body>
-<table class="table table-bordered table-striped">
-<thead class="bg-dark text-white header-row">
-<tr>
-	<th>exposure process reported</th>
-	<th>exposure material reported</th>
-	<th>exposure material id</th>
-	<th>disease reported</th>
-	<th>disease ontology id</th>
-	<th>disease stage reported</th>
-</tr>
-</thead>
-	<tr>
-		<td><a href="ONTIE:0003310">unknown</a></td>
-		<td></td>
-		<td></td>
-		<td></td>
-		<td></td>
-		<td></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003314">administering substance in vivo</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_11103">Hepacivirus C</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_11103">NCBITaxon:11103</a></td>
-		<td></td>
-		<td></td>
-		<td></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003305">exposure to substance without evidence for disease</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">Dengue virus</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">NCBITaxon:12637</a></td>
-		<td></td>
-		<td></td>
-		<td></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003322">occurrence of disease</a></td>
-		<td></td>
-		<td></td>
-		<td></td>
-		<td></td>
-		<td></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003317">occurrence of infectious disease</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">Dengue virus</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">NCBITaxon:12637</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/DOID_12206">dengue hemorrhagic fever</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/DOID_12206">DOID:12206</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/OGMS_0000094">Acute/Recent onset</a></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003317">occurrence of infectious disease</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">Dengue virus</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">NCBITaxon:12637</a></td>
-		<td class="bg-danger" data-toggle="tooltip" data-placement="right" title="Cell is empty but rule: &quot;is-required true&quot; does not allow this."></td>
-		<td></td>
-		<td><a href="http://purl.obolibrary.org/obo/OGMS_0000094">Acute/Recent onset</a></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003317">occurrence of infectious disease</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">Dengue virus</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">NCBITaxon:12637</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/DOID_12206">dengue hemorrhagic fever</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/DOID_12206">DOID:12206</a></td>
-		<td class="bg-danger" data-toggle="tooltip" data-placement="right" title="Cell is empty but rule: &quot;is-required true&quot; does not allow this."></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003317">occurrence of infectious disease</a></td>
-		<td class="bg-danger" data-toggle="tooltip" data-placement="right" title="Validation failed for rule: &quot;Dengue virus equivalent-to (NCBITaxon:11060)&quot;."><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">Dengue virus</a></td>
-		<td class="bg-danger" data-toggle="tooltip" data-placement="right" title="Validation failed for rule: &quot;NCBITaxon:11060 equivalent-to 'Dengue virus'&quot;."><a href="http://purl.obolibrary.org/obo/NCBITaxon_11060">NCBITaxon:11060</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/DOID_12206">dengue hemorrhagic fever</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/DOID_12206">DOID:12206</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/OGMS_0000094">Acute/Recent onset</a></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003317">occurrence of infectious disease</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">Dengue virus</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">NCBITaxon:12637</a></td>
-		<td class="bg-danger" data-toggle="tooltip" data-placement="right" title="Validation failed for rule: &quot;Dengue virus 2 subclass-of disease&quot;.; Validation failed for rule: &quot;Dengue virus 2 subclass-of 'has material basis in' some 'Dengue virus'&quot;."><a href="http://purl.obolibrary.org/obo/NCBITaxon_11060">Dengue virus 2</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_11060">NCBITaxon:11060</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/OGMS_0000094">Acute/Recent onset</a></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003305">'exposure to substance without evidence for disease'</a> or <a href="ONTIE:0003314">'administering substance in vivo'</a></td>
-		<td class="bg-danger" data-toggle="tooltip" data-placement="right" title="Validation failed for rule: &quot;blood or 'Hepacivirus C' equivalent-to (UBERON:0000178)&quot;."><a href="http://purl.obolibrary.org/obo/NCBITaxon_11103">'Hepacivirus C'</a> or <a href="http://purl.obolibrary.org/obo/UBERON_0000178">blood</a></td>
-		<td class="bg-danger" data-toggle="tooltip" data-placement="right" title="Validation failed for rule: &quot;UBERON:0000178 equivalent-to (blood or 'Hepacivirus C')&quot;."><a href="http://purl.obolibrary.org/obo/UBERON_0000178">UBERON:0000178</a></td>
-		<td></td>
-		<td></td>
-		<td class="bg-danger" data-toggle="tooltip" data-placement="right" title="Cell is non-empty (&quot;Chronic&quot;) but rule: &quot;is-excluded true&quot; does not allow this."><a href="http://purl.obolibrary.org/obo/OGMS_000094">Chronic</a></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003317">occurrence of infectious disease</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">Dengue virus</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">NCBITaxon:12637</a></td>
-		<td class="bg-danger" data-toggle="tooltip" data-placement="right" title="Validation failed for rule: &quot;wheat allergy equivalent-to (DOID:12206)&quot;.; Validation failed for rule: &quot;dengue hemorrhagic fever equivalent-to (DOID:3660)&quot;.; Validation failed for rule: &quot;wheat allergy subclass-of 'has material basis in' some 'Dengue virus'&quot;."><a href="http://purl.obolibrary.org/obo/DOID_12206">dengue hemorrhagic fever</a>|<a href="http://purl.obolibrary.org/obo/DOID_3660">wheat allergy</a></td>
-		<td class="bg-danger" data-toggle="tooltip" data-placement="right" title="Validation failed for rule: &quot;DOID:3660 equivalent-to 'dengue hemorrhagic fever'&quot;.; Validation failed for rule: &quot;DOID:12206 equivalent-to 'wheat allergy'&quot;."><a href="http://purl.obolibrary.org/obo/DOID_12206">DOID:12206</a>|<a href="http://purl.obolibrary.org/obo/DOID_3660">DOID:3660</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/OGMS_0000094">Acute/Recent onset</a></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003317">occurrence of infectious disease</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">Dengue virus</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">NCBITaxon:12637</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/DOID_12206">dengue hemorrhagic fever</a>|<a href="http://purl.obolibrary.org/obo/DOID_12206">dengue hemorrhagic fever</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/DOID_12206">DOID:12206</a>|<a href="http://purl.obolibrary.org/obo/DOID_12206">DOID:12206</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/OGMS_0000094">Acute/Recent onset</a></td>
-	</tr>
-</table>
-</body>
-<script>
-	$(function () {
-		$('[data-toggle="tooltip"]').tooltip()
-	})</script>
diff --git a/docs/examples/immune_exposures.owl b/docs/examples/immune_exposures.owl
deleted file mode 100644
index cf7472de..00000000
--- a/docs/examples/immune_exposures.owl
+++ /dev/null
@@ -1,709 +0,0 @@
-<?xml version="1.0"?>
-<rdf:RDF xmlns="http://www.w3.org/2002/07/owl#"
-     xml:base="http://www.w3.org/2002/07/owl"
-     xmlns:owl="http://www.w3.org/2002/07/owl#"
-     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:xml="http://www.w3.org/XML/1998/namespace"
-     xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
-     xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
-    <Ontology/>
-    
-
-
-    <!-- 
-    ///////////////////////////////////////////////////////////////////////////////////////
-    //
-    // Object Properties
-    //
-    ///////////////////////////////////////////////////////////////////////////////////////
-     -->
-
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/IDO_0000664 -->
-
-    <ObjectProperty rdf:about="http://purl.obolibrary.org/obo/IDO_0000664">
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">has material basis in</rdfs:label>
-    </ObjectProperty>
-    
-
-
-    <!-- 
-    ///////////////////////////////////////////////////////////////////////////////////////
-    //
-    // Classes
-    //
-    ///////////////////////////////////////////////////////////////////////////////////////
-     -->
-
-    
-
-
-    <!-- ONTIE:0003304 -->
-
-    <Class rdf:about="ONTIE:0003304">
-        <rdfs:subClassOf rdf:resource="ONTIE:0003314"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Adminstering a vaccine to an organism with the intention of inducing immunity against antigen components of the vaccine.</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">vaccination</rdfs:label>
-        <rdfs:seeAlso rdf:datatype="http://www.w3.org/2001/XMLSchema#string">http://purl.obolibrary.org/obo/VO_0000002</rdfs:seeAlso>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003305 -->
-
-    <Class rdf:about="ONTIE:0003305">
-        <rdfs:subClassOf rdf:resource="http://example.com/IE_1"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">An unplanned process in which an organism comes into contact with a substance without evidence for a disease caused by that exposure.</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">exposure to substance without evidence for disease</rdfs:label>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003306 -->
-
-    <Class rdf:about="ONTIE:0003306">
-        <rdfs:subClassOf rdf:resource="ONTIE:0003305"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">A process in which an infectious agent is in or on the body of an organism without causing detectable disease .</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">occurrence of asymptomatic infection</rdfs:label>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003307 -->
-
-    <Class rdf:about="ONTIE:0003307">
-        <rdfs:subClassOf rdf:resource="ONTIE:0003305"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">A process in which an organism is exposed to a substance which is evident from that process having been observed or documented.</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">documented exposure without evidence for disease</rdfs:label>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003308 -->
-
-    <Class rdf:about="ONTIE:0003308">
-        <rdfs:subClassOf rdf:resource="ONTIE:0003305"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">A process in which an organism&apos;s exposure to a material entity is assumed from that material being commonly present in the environment of the organism.</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">environmental exposure to endemic/ubiquitous agent without evidence for disease</rdfs:label>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003309 -->
-
-    <Class rdf:about="ONTIE:0003309">
-        <rdfs:subClassOf rdf:resource="http://example.com/IE_1"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">An organism&apos;s lifspan which does not include exposure to a substance of interest.</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">no exposure</rdfs:label>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003310 -->
-
-    <Class rdf:about="ONTIE:0003310">
-        <rdfs:subClassOf rdf:resource="http://example.com/IE_1"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">An organism&apos;s lifespan for which there is no available information on an exposure to a material entity of interest.</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">unknown</rdfs:label>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003311 -->
-
-    <Class rdf:about="ONTIE:0003311">
-        <rdfs:subClassOf rdf:resource="ONTIE:0003316"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">A planned process in which solid tissue is transferred to an organism</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">solid tissue transplantation</rdfs:label>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003312 -->
-
-    <Class rdf:about="ONTIE:0003312">
-        <rdfs:subClassOf rdf:resource="ONTIE:0003316"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">A planned process in which a bodily fluid is transferred into an organism</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">transfusion</rdfs:label>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003313 -->
-
-    <Class rdf:about="ONTIE:0003313">
-        <rdfs:subClassOf rdf:resource="ONTIE:0003320"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">An occurrence of cancer where there is evidence for the presence of a cancer causing oncovirus in the tumor.</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">occurrence of cancer associated with virus</rdfs:label>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003314 -->
-
-    <Class rdf:about="ONTIE:0003314">
-        <rdfs:subClassOf rdf:resource="http://example.com/IE_1"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">A planned process by which a material is intentionally given to an organism resulting in exposure of the organism to that substance.</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">administering substance in vivo</rdfs:label>
-        <rdfs:seeAlso rdf:datatype="http://www.w3.org/2001/XMLSchema#string">http://purl.obolibrary.org/obo/OBI_0600007</rdfs:seeAlso>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003315 -->
-
-    <Class rdf:about="ONTIE:0003315">
-        <rdfs:subClassOf rdf:resource="ONTIE:0003314"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Administering an infectious agent to an organism in order to test if and how an infection will occur.</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">infectious challenge</rdfs:label>
-        <rdfs:seeAlso rdf:datatype="http://www.w3.org/2001/XMLSchema#string">http://purl.obolibrary.org/obo/OBI_0000712</rdfs:seeAlso>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003316 -->
-
-    <Class rdf:about="ONTIE:0003316">
-        <rdfs:subClassOf rdf:resource="ONTIE:0003314"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Transferring a solid tissue (transplant) or bodily fluid (transfusion) to an organism.</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">transplant or transfusion</rdfs:label>
-        <rdfs:seeAlso rdf:datatype="http://www.w3.org/2001/XMLSchema#string">http://purl.obolibrary.org/obo/OBI_0000105</rdfs:seeAlso>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003317 -->
-
-    <Class rdf:about="ONTIE:0003317">
-        <rdfs:subClassOf rdf:resource="ONTIE:0003322"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">The process in which an infectious disease unfolds.</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">occurrence of infectious disease</rdfs:label>
-        <rdfs:seeAlso rdf:datatype="http://www.w3.org/2001/XMLSchema#string">http://purl.obolibrary.org/obo/DOID_0050117</rdfs:seeAlso>
-        <rdfs:seeAlso rdf:datatype="http://www.w3.org/2001/XMLSchema#string">http://purl.obolibrary.org/obo/OBI_1110008</rdfs:seeAlso>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003318 -->
-
-    <Class rdf:about="ONTIE:0003318">
-        <rdfs:subClassOf rdf:resource="ONTIE:0003322"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">The process in which an allergic disease unfolds.</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">occurrence of allergic disease</rdfs:label>
-        <rdfs:seeAlso rdf:datatype="http://www.w3.org/2001/XMLSchema#string">http://purl.obolibrary.org/obo/DOID_1205</rdfs:seeAlso>
-        <rdfs:seeAlso rdf:datatype="http://www.w3.org/2001/XMLSchema#string">http://purl.obolibrary.org/obo/OBI_1110012</rdfs:seeAlso>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003319 -->
-
-    <Class rdf:about="ONTIE:0003319">
-        <rdfs:subClassOf rdf:resource="ONTIE:0003322"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">The process in which an autoimmune disease unfolds.</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">occurrence of autoimmune disease</rdfs:label>
-        <rdfs:seeAlso rdf:datatype="http://www.w3.org/2001/XMLSchema#string">http://purl.obolibrary.org/obo/DOID_417</rdfs:seeAlso>
-        <rdfs:seeAlso rdf:datatype="http://www.w3.org/2001/XMLSchema#string">http://purl.obolibrary.org/obo/OBI_1110054</rdfs:seeAlso>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003320 -->
-
-    <Class rdf:about="ONTIE:0003320">
-        <rdfs:subClassOf rdf:resource="ONTIE:0003322"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">The process in which cancer unfolds</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">occurrence of cancer</rdfs:label>
-        <rdfs:seeAlso rdf:datatype="http://www.w3.org/2001/XMLSchema#string">http://purl.obolibrary.org/obo/DOID_162</rdfs:seeAlso>
-        <rdfs:seeAlso rdf:datatype="http://www.w3.org/2001/XMLSchema#string">http://purl.obolibrary.org/obo/OBI_1110053</rdfs:seeAlso>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003321 -->
-
-    <Class rdf:about="ONTIE:0003321">
-        <rdfs:subClassOf rdf:resource="ONTIE:0003305"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">A process in which an organism is exposed to a material entity which is evident by a detectable immune reactivity against it.</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">exposure with existing immune reactivity without evidence for disease</rdfs:label>
-        <rdfs:seeAlso rdf:datatype="http://www.w3.org/2001/XMLSchema#string">http://purl.obolibrary.org/obo/OBI_1110061</rdfs:seeAlso>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003322 -->
-
-    <Class rdf:about="ONTIE:0003322">
-        <rdfs:subClassOf rdf:resource="http://example.com/IE_1"/>
-        <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">The process in which a disease unfolds.</rdfs:comment>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">occurrence of disease</rdfs:label>
-        <rdfs:seeAlso rdf:datatype="http://www.w3.org/2001/XMLSchema#string">http://purl.obolibrary.org/obo/DOID_4</rdfs:seeAlso>
-        <rdfs:seeAlso rdf:datatype="http://www.w3.org/2001/XMLSchema#string">http://purl.obolibrary.org/obo/OGMS_0000031</rdfs:seeAlso>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003544 -->
-
-    <Class rdf:about="ONTIE:0003544">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/OBI_0000278"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Post</rdfs:label>
-    </Class>
-    
-
-
-    <!-- ONTIE:0003545 -->
-
-    <Class rdf:about="ONTIE:0003545">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/OBI_0000278"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Unknown</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://example.com/IE_1 -->
-
-    <Class rdf:about="http://example.com/IE_1">
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">exposure process</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/BFO_0000040 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/BFO_0000040">
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">material entity</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/DOID_1205 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/DOID_1205">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/DOID_4"/>
-        <rdfs:label>allergic disease</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/DOID_12206 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/DOID_12206">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/DOID_4"/>
-        <rdfs:subClassOf>
-            <Restriction>
-                <onProperty rdf:resource="http://purl.obolibrary.org/obo/IDO_0000664"/>
-                <someValuesFrom rdf:resource="http://purl.obolibrary.org/obo/NCBITaxon_12637"/>
-            </Restriction>
-        </rdfs:subClassOf>
-        <rdfs:label>dengue hemorrhagic fever</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/DOID_162 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/DOID_162">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/DOID_4"/>
-        <rdfs:label>cancer</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/DOID_219 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/DOID_219">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/DOID_162"/>
-        <rdfs:label>colon cancer</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/DOID_3660 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/DOID_3660">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/DOID_1205"/>
-        <rdfs:subClassOf>
-            <Restriction>
-                <onProperty rdf:resource="http://purl.obolibrary.org/obo/IDO_0000664"/>
-                <someValuesFrom rdf:resource="http://purl.obolibrary.org/obo/NCBITaxon_4565"/>
-            </Restriction>
-        </rdfs:subClassOf>
-        <rdfs:label>wheat allergy</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/DOID_4 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/DOID_4">
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">disease</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/DOID_417 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/DOID_417">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/DOID_4"/>
-        <rdfs:label>autoimmune disease</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/DOID_635 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/DOID_635">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/DOID_417"/>
-        <rdfs:label>acquired immunodeficiency syndrome</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/NCBITaxon_1 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/NCBITaxon_1">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/BFO_0000040"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">organism</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/NCBITaxon_11053 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/NCBITaxon_11053">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/NCBITaxon_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Dengue virus 1</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/NCBITaxon_11060 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/NCBITaxon_11060">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/NCBITaxon_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Dengue virus 2</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/NCBITaxon_11103 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/NCBITaxon_11103">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/NCBITaxon_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Hepacivirus C</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/NCBITaxon_12637 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/NCBITaxon_12637">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/NCBITaxon_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Dengue virus</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/NCBITaxon_4565 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/NCBITaxon_4565">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/NCBITaxon_1"/>
-        <rdfs:label>Triticum aestivum</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/OBI_0000278 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/OBI_0000278">
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">disease stage</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/OGMS_0000094 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/OGMS_0000094">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/OBI_0000278"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Acute/Recent onset</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/OGMS_000094 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/OGMS_000094">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/OBI_0000278"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Chronic</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/UBERON_0000178 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/UBERON_0000178">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/BFO_0000040"/>
-        <rdfs:label>blood</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0000035 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0000035">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Dryvax</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0000044 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0000044">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">FluMist</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0000045 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0000045">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Fluarix</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0000046 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0000046">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Fluvirin</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0000047 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0000047">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Fluzone</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0000071 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0000071">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Menactra</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0000081 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0000081">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Influenza A (H1N1) 2009 Monovalent Vaccine (Novartis)</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0000087 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0000087">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Plasmodium falciparum vaccine</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0000088 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0000088">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Pneumovax 23 (USA)</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0000121 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0000121">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">YF-Vax</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0000122 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0000122">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Yellow fever 17D vaccine vector</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0000124 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0000124">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Zostavax</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0000642 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0000642">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Influenza virus vaccine</rdfs:label>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Trivalent inactivated influenza</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0000669 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0000669">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Varicella-zoster virus vaccine</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0000771 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0000771">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">BCG Vaccine</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0001246 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0001246">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Menveo</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0003120 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0003120">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">MVA85A</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0003133 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0003133">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">MRKAd5 HIV-1 gag/pol/nef</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0003138 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0003138">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">ACWY Vax</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0003139 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0003139">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Stamaril</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0003152 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0003152">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">HEPLISAV-B</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0004091 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0004091">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">LC16m8</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0004660 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0004660">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">rVSV-EBOV</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0004809 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0004809">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">2008-2009 trivalent influenza vaccine</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0010711 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0010711">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Engerix-B</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0010725 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_0010725">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_1"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Meningococcal Polysaccharide Vaccine, Groups A &amp; C, Menomune A/C</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_1 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/VO_1">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/BFO_0000040"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">vaccine</rdfs:label>
-    </Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/XCO_0000248 -->
-
-    <Class rdf:about="http://purl.obolibrary.org/obo/XCO_0000248">
-        <rdfs:subClassOf rdf:resource="ONTIE:0003316"/>
-        <rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">kidney transplant</rdfs:label>
-    </Class>
-</rdf:RDF>
-
-
-
-<!-- Generated by the OWL API (version 4.5.9.2019-02-01T07:24:44Z) https://github.com/owlcs/owlapi -->
-
diff --git a/docs/examples/immune_exposures.txt b/docs/examples/immune_exposures.txt
deleted file mode 100644
index 596b01ac..00000000
--- a/docs/examples/immune_exposures.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-At immune_exposures.csv row 7, column 4: Cell is empty but rule: "is-required true" does not allow this.
-At immune_exposures.csv row 8, column 6: Cell is empty but rule: "is-required true" does not allow this.
-At immune_exposures.csv row 9, column 2: Validation failed for rule: "Dengue virus equivalent-to (NCBITaxon:11060)".
-At immune_exposures.csv row 9, column 3: Validation failed for rule: "NCBITaxon:11060 equivalent-to 'Dengue virus'".
-At immune_exposures.csv row 10, column 4: Validation failed for rule: "Dengue virus 2 subclass-of disease".
-At immune_exposures.csv row 10, column 4: Validation failed for rule: "Dengue virus 2 subclass-of 'has material basis in' some 'Dengue virus'".
-At immune_exposures.csv row 11, column 2: Validation failed for rule: "blood or 'Hepacivirus C' equivalent-to (UBERON:0000178)".
-At immune_exposures.csv row 11, column 3: Validation failed for rule: "UBERON:0000178 equivalent-to (blood or 'Hepacivirus C')".
-At immune_exposures.csv row 11, column 6: Cell is non-empty ("Chronic") but rule: "is-excluded true" does not allow this.
-At immune_exposures.csv row 12, column 4: Validation failed for rule: "wheat allergy equivalent-to (DOID:12206)".
-At immune_exposures.csv row 12, column 4: Validation failed for rule: "dengue hemorrhagic fever equivalent-to (DOID:3660)".
-At immune_exposures.csv row 12, column 4: Validation failed for rule: "wheat allergy subclass-of 'has material basis in' some 'Dengue virus'".
-At immune_exposures.csv row 12, column 5: Validation failed for rule: "DOID:3660 equivalent-to 'dengue hemorrhagic fever'".
-At immune_exposures.csv row 12, column 5: Validation failed for rule: "DOID:12206 equivalent-to 'wheat allergy'".
diff --git a/docs/examples/immune_exposures_2.csv b/docs/examples/immune_exposures_2.csv
deleted file mode 100644
index c5c1af42..00000000
--- a/docs/examples/immune_exposures_2.csv
+++ /dev/null
@@ -1,13 +0,0 @@
-exposure process reported,exposure material reported,exposure material id,disease reported,disease ontology id,disease stage reported
-vaccination,Varicella-zoster virus vaccine,VO:0000669,,,
-infectious challenge,Hepacivirus C,NCBITaxon:11103,,,
-transplant or transfusion,blood,UBERON:0000178,,,
-infectious challenge,Dengue virus,NCBITaxon:12637,dengue hemorrhagic fever,DOID:12206,Acute/Recent onset
-allergic disease,Triticum aestivum,NCBITaxon:4565,wheat allergy,DOID:3660,Post
-occurrence of autoimmune disease,,,acquired immunodeficiency syndrome,DOID:635,Chronic
-occurrence of cancer,,,colon cancer,DOID:219,Chronic
-occurrence of asymptomatic infection,Dengue virus,NCBITaxon:12637,,,
-exposure with existing immune reactivity without evidence for disease,Dengue virus 2,NCBITaxon:11060,,,
-documented exposure without evidence for disease,Dengue virus,NCBITaxon:12637,,,Post
-environmental exposure to endemic/ubiquitous agent without evidence for disease,Dengue virus,NCBITaxon:12637,,,
-no exposure,,,,,
\ No newline at end of file
diff --git a/docs/examples/immune_exposures_2.html b/docs/examples/immune_exposures_2.html
deleted file mode 100644
index 631de0c6..00000000
--- a/docs/examples/immune_exposures_2.html
+++ /dev/null
@@ -1,120 +0,0 @@
-<head>
-	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
-	<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
-	<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
-	<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
-</head>
-<body>
-<table class="table table-bordered table-striped">
-<thead class="bg-dark text-white header-row">
-<tr>
-	<th>exposure process reported</th>
-	<th>exposure material reported</th>
-	<th>exposure material id</th>
-	<th>disease reported</th>
-	<th>disease ontology id</th>
-	<th>disease stage reported</th>
-</tr>
-</thead>
-	<tr>
-		<td><a href="ONTIE:0003304">vaccination</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/VO_0000669">Varicella-zoster virus vaccine</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/VO_0000669">VO:0000669</a></td>
-		<td></td>
-		<td></td>
-		<td></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003315">infectious challenge</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_11103">Hepacivirus C</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_11103">NCBITaxon:11103</a></td>
-		<td></td>
-		<td></td>
-		<td></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003316">transplant or transfusion</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/UBERON_0000178">blood</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/UBERON_0000178">UBERON:0000178</a></td>
-		<td></td>
-		<td></td>
-		<td></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003315">infectious challenge</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">Dengue virus</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">NCBITaxon:12637</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/DOID_12206">dengue hemorrhagic fever</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/DOID_12206">DOID:12206</a></td>
-		<td class="bg-danger" data-toggle="tooltip" data-placement="right" title="Cell is non-empty (&quot;Acute/Recent onset&quot;) but rule: &quot;is-excluded true&quot; does not allow this."><a href="http://purl.obolibrary.org/obo/OGMS_0000094">Acute/Recent onset</a></td>
-	</tr>
-	<tr>
-		<td class="bg-danger" data-toggle="tooltip" data-placement="right" title="Validation failed for rule: &quot;allergic disease subclass-of 'exposure process'&quot;."><a href="http://purl.obolibrary.org/obo/DOID_1205">allergic disease</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_4565">Triticum aestivum</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_4565">NCBITaxon:4565</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/DOID_3660">wheat allergy</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/DOID_3660">DOID:3660</a></td>
-		<td class="bg-danger" data-toggle="tooltip" data-placement="right" title="Cell is non-empty (&quot;Post&quot;) but rule: &quot;is-excluded true&quot; does not allow this."><a href="ONTIE:0003544">Post</a></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003319">occurrence of autoimmune disease</a></td>
-		<td></td>
-		<td></td>
-		<td><a href="http://purl.obolibrary.org/obo/DOID_635">acquired immunodeficiency syndrome</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/DOID_635">DOID:635</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/OGMS_000094">Chronic</a></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003320">occurrence of cancer</a></td>
-		<td></td>
-		<td></td>
-		<td><a href="http://purl.obolibrary.org/obo/DOID_219">colon cancer</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/DOID_219">DOID:219</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/OGMS_000094">Chronic</a></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003306">occurrence of asymptomatic infection</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">Dengue virus</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">NCBITaxon:12637</a></td>
-		<td></td>
-		<td></td>
-		<td></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003321">exposure with existing immune reactivity without evidence for disease</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_11060">Dengue virus 2</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_11060">NCBITaxon:11060</a></td>
-		<td></td>
-		<td></td>
-		<td></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003307">documented exposure without evidence for disease</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">Dengue virus</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">NCBITaxon:12637</a></td>
-		<td></td>
-		<td></td>
-		<td class="bg-danger" data-toggle="tooltip" data-placement="right" title="Cell is non-empty (&quot;Post&quot;) but rule: &quot;is-excluded true&quot; does not allow this."><a href="ONTIE:0003544">Post</a></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003308">environmental exposure to endemic/ubiquitous agent without evidence for disease</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">Dengue virus</a></td>
-		<td><a href="http://purl.obolibrary.org/obo/NCBITaxon_12637">NCBITaxon:12637</a></td>
-		<td></td>
-		<td></td>
-		<td></td>
-	</tr>
-	<tr>
-		<td><a href="ONTIE:0003309">no exposure</a></td>
-		<td></td>
-		<td></td>
-		<td></td>
-		<td></td>
-		<td></td>
-	</tr>
-</table>
-</body>
-<script>
-	$(function () {
-		$('[data-toggle="tooltip"]').tooltip()
-	})</script>
diff --git a/docs/examples/immune_exposures_rules.csv b/docs/examples/immune_exposures_rules.csv
deleted file mode 100644
index aabbed92..00000000
--- a/docs/examples/immune_exposures_rules.csv
+++ /dev/null
@@ -1,13 +0,0 @@
-table,column,validation
-immune_exposures,exposure process reported,is-required; subclass-of 'exposure process';
-immune_exposures,exposure material reported,subclass-of|equivalent-to 'material entity'; equivalent-to {exposure material id}; is-required (when {exposure process reported} subclass-of ('administering substance in vivo' or 'exposure to substance without evidence for disease' or 'occurrence of infectious disease' or 'occurrence of allergic disease')); is-excluded (when {exposure process reported} equivalent-to 'occurrence of disease'); is-excluded (when {exposure process reported} subclass-of ('occurrence of autoimmune disease' or 'occurrence of cancer' or 'no exposure' or unknown))
-immune_exposures,exposure material id,equivalent-to {exposure material reported}
-immune_exposures,disease reported,subclass-of disease; subclass-of 'has material basis in' some {exposure material reported} (when {exposure process reported} not-subclass-of ('occurrence of autoimmune disease' or 'occurrence of cancer')); equivalent-to {disease ontology id}; is-required (when {exposure process reported} subclass-of 'occurrence of disease'); is-excluded (when {exposure process reported} equivalent-to 'administering substance in vivo'); is-excluded (when {exposure process reported} subclass-of (vaccination or 'transplant or transfusion' or 'exposure to substance without evidence for disease' or 'no exposure' or unknown))
-immune_exposures,disease ontology id,equivalent-to {disease reported}
-immune_exposures,disease stage reported,subclass-of 'disease stage'; is-required (when {exposure process reported} subclass-of 'occurrence of disease'); is-excluded (when {exposure process reported} not-subclass-of 'occurrence of disease')
-immune_exposures_2,exposure process reported,is-required; subclass-of 'exposure process';
-immune_exposures_2,exposure material reported,subclass-of|equivalent-to 'material entity'; equivalent-to {exposure material id}; is-required (when {exposure process reported} subclass-of ('administering substance in vivo' or 'exposure to substance without evidence for disease' or 'occurrence of infectious disease' or 'occurrence of allergic disease')); is-excluded (when {exposure process reported} equivalent-to 'occurrence of disease'); is-excluded (when {exposure process reported} subclass-of ('occurrence of autoimmune disease' or 'occurrence of cancer' or 'no exposure' or unknown))
-immune_exposures_2,exposure material id,equivalent-to {exposure material reported}
-immune_exposures_2,disease reported,subclass-of disease; subclass-of 'has material basis in' some {exposure material reported} (when {exposure process reported} not-subclass-of ('occurrence of autoimmune disease' or 'occurrence of cancer')); equivalent-to {disease ontology id}; is-required (when {exposure process reported} subclass-of 'occurrence of disease'); is-excluded (when {exposure process reported} equivalent-to 'administering substance in vivo'); is-excluded (when {exposure process reported} subclass-of (vaccination or 'transplant or transfusion' or 'exposure to substance without evidence for disease' or 'no exposure' or unknown))
-immune_exposures_2,disease ontology id,equivalent-to {disease reported}
-immune_exposures_2,disease stage reported,subclass-of 'disease stage'; is-required (when {exposure process reported} subclass-of 'occurrence of disease'); is-excluded (when {exposure process reported} not-subclass-of 'occurrence of disease')
diff --git a/docs/validate.md b/docs/validate.md
deleted file mode 100644
index c6385c0c..00000000
--- a/docs/validate.md
+++ /dev/null
@@ -1,348 +0,0 @@
-# Validate
-
-- [Overview](#overview)
-    - [Formats](#formats)
-    - [Other configuration](#other-configuration)
-- [Input file organisation](#input-file-organisation)
-    - [Validation rules](#validation-rules)
-    - [Comments](#comments)
-    - [Cell data](#cell-data)
-    - [Multi-value cells](#multi-value-cells)
-- [Validation rule syntax](#validation-rule-syntax)
-    - [Rule types](#rule-types)
-    - [Substitution](#substitution)
-    - [When-clauses](#when-clauses)
-    - [Compound rule-types](#compound-rule-types)
-- [Error Messages](#error-messages)
-
-## Overview
-
-Validates tables (CSV or TSV files) (`--table`) against a reasoned input ontology (`--input`) using the sets of rules defined in a rules table (`--rules`). The reasoner used is specified by the `--reasoner` option, which defaults to ELK. We recommend using the HermiT reasoner, as it supports generalized class expression queries. This command writes the output to TXT, HTML, or XLSX files in the output directory (`--output-dir`) with the same base filename. If no output format is specified then the output is directed to STDOUT. For example:
-
-    robot validate --input immune_exposures.owl \
-      --rules immune_exposures_rules.csv \
-      --table immune_exposures.csv \
-      --reasoner hermit \
-      --no-fail true \
-      --format TXT \
-      --output-dir results/
-
-In this case the command will generate a single file called `immune_exposures.txt` in the `results/` directory.
-
-One can also specify multiple table files to validate against a single input ontology. In that case there will be multiple output files corresponding to each table in the output directory. For example:
-
-    robot validate --input immune_exposures.owl \
-      --rules immune_exposures_rules.csv \
-      --table immune_exposures.csv \
-      --table immune_exposures_2.csv \
-      --reasoner hermit \
-      --no-fail true \
-      --format HTML \
-      --output-dir results/
-
-In this case two files: `immune_exposures.html` and `immune_exposures_2.html` will appear in the `results/` directory.
-
-Finally, you can specify a whole directory to validate against a single input ontology. The command will generate the text outputs matching all table names in the `results/` directory. Note that a table will only be validated if it appears in the rules table.
-
-```
-robot validate --input immune_exposures.owl \
-  --rules immune_exposures_rules.csv \
-  --tables tables/ \
-  --reasoner hermit \
-  --format TXT \
-  --output-dir results/
-```
-
-### Rules Table
-
-The rules table contains the validation rules. This table should be either TSV or CSV and must have three required columns:
-
-* `table`: name of table to validate
-* `column`: name of column to validate
-* `validation`: the validation rule (see [Validation rule syntax](#validation-rule-syntax))
-
-You can include as many extra columns as you would like (e.g., comments), but these columns will be ignored.
-
-### Formats
-
-* `txt`: a list of failed validations. E.g.:
-```
-At immune_exposures.csv row 17, column 2: Cell is empty but rule: "is-required true" does not allow this.
-```
-* `html`: a [Bootstrap](https://getbootstrap.com/) HTML version of the `--table`. Cells containing bad data (failed validations) are highlighted red. Hovering over the red cells shows a tooltip with the message. These tables use CSS and JavaScript plugins from the BootstrapCDN, therefore they require an internet connection to properly view the table. See [HTML Tables](#html-tables-standalone) for more details.
-* `xlsx`: an Excel spreadsheet version of the `--table`. Cells containing bad data are highlighted red and have a Comment on them containing the message.
-
-### Other options
-
-#### Exit Codes (`--no-fail`)
-
-If there are any invalid cells, `validate` will fail by default with exit code `1`. This is good for use in [`Makefile` workflows](/make), as it will stop the workflow when there is a non-zero exit code. You can override this with `--no-fail true` as shown in the above examples if you want to bypass failures and always exit with code `0`.
-
-#### Logging (`--silent`)
-
-`validate` will only print a summary message at the end if there were any failures (unless no `--format` is specified, in which case it will always print to STDOUT). If you would like to print all invalid data messages, include `--silent false`.
-
-#### Output Files (`--write-all`)
-
-`validate` will only write tables with failed validations to the output directory. If you wish to write _all_ tables, including those that did not have any failed validation, specify `--write-all true`.
-
-#### HTML Tables (`--standalone`)
-
-If the output format is HTML, all output tables will be written as "standalone" files. This means that they have a header containing the Bootstrap stylesheet and scripts ([for tooltips](https://getbootstrap.com/docs/4.5/components/tooltips)). If you want to plug the table data into an existing HTML file, you can use `--standalone false` to generate _just_ the table element.
-
-Note that the tooltips and styling will not work until the table is inserted into a file containing the required CSS and JavaScript from [BootstrapCDN](https://getbootstrap.com/docs/4.5/getting-started/introduction/). For offline viewing, you can also [download](https://getbootstrap.com/docs/4.5/getting-started/download/) the required files and provide a local path in the HTML header. The Bootstrap download does not include [jQuery](https://jquery.com/download/) or [Popper.js](https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js), which are required to enable tooltips (as well as a [small piece of JavaScript](https://getbootstrap.com/docs/4.5/components/tooltips/#example-enable-tooltips-everywhere)).
-
-#### Errors Table (`--errors`)
-
-The results that are written to the output directory contain all lines from the input tables, even if those lines don't have errors. You can choose to also output a table containing just the errors by specifying `--errors <path>`:
-
-    robot validate --input immune_exposures.owl \
-      --rules immune_exposures_rules.csv \
-      --table immune_exposures.csv \
-      --reasoner hermit \
-      --no-fail true \
-      --errors results/errors.csv
-
-If this path ends with `.csv`, the output will be comma-separated. Otherwise, the output will be tab-separated.
-
-This output will contain the following columns:
-* **table**: the name of the table this cell is in
-* **cell**: the A1 notation for the location of the cell
-* **rule**: the rule type (e.g., `is-required`)
-* **message**: text description of the violation
-
-## Input file organisation
-
-### Validation rules
-
-Validation rules are read from the rules table. One or more validation rule can be specified for each column, separated by a semi-colon (`;`), and these rules are applied only to the data in that column. Each rule for a column will be validated independently, and if any one of those rules is violated, the data in the cell will be considered invalid (i.e., the data must pass all rules). For details on using rules as "OR" statements (i.e., the data must pass at least one rule), see [Compound rule-types](#compound-rule-types).
-
-### Comments
-
-To comment out all of the rules for a given column, the list should be prefixed by '##'. To comment out particular rules from among the rules belonging to a given column, prefix those rules with '#'. For example:
-
-_To comment out all rules:_
-```
-## rule 1; rule 2; rule 3
-```
-
-_To comment out rule 1 but not rule 2:_
-```
-# rule 1; rule 2
-```
-
-_To comment out rule 2 but not rule 1:_
-```
-rule 1; # rule 2
-```
-
-### Cell data
-
-Data cells must either be in the form of a named class, e.g. 'Dengue virus', a named individual, e.g. 'Dr. Smith', or a general class expression, e.g. ('Dengue virus' or 'Dengue virus 2'). IRIs or CURIEs may be be used in lieu of labels if desired.
-
-When using labels within class expressions, the label must be enclosed in single quotes if it contains a space. No quotes should be used for single-word labels (e.g. `virus`). If you are just referring to a named class or individual, you do not need to enclose the label in single quotes.
-
-### Multi-value cells
-
-A data cell can contain more than one logical entity if these are separated using the pipe ('|') character. Such cells are called multi-value cells. When a rule is defined over a multi-value cell, it will be validated for each logical entity in that multi-value cell, and if the rule contains a [wildcard](#wildcards) that refers to a multi-value cell, then all possible interpretations of that rule will be validatd against the current cell (which may itself be a multi-value cell). Consider, for example, the rule `subclass-of %1` defined for column `header 2`:
-
-|header 1                        |header 2                        |
-|--------------------------------|--------------------------------|
-|data1A \| data1B                |data2A \| data2B                |
-|...                             |                                |
-
-In this case, the following validations will be performed:
-
-* data2A subclass-of data1A
-* data2A subclass-of data1B
-* data2B subclass-of data1A
-* data2B subclass-of data1B
-
-## Validation rule syntax
-
-Individual rules must be of the form:
-
-	<main-rule-type> <value> [(when <when-subject-expr-1> <when-rule-type-1> <when-value-1> & ...)]
-
-Where:
-
-* `<main-rule-type>` can be one of (or a combination of -- see [Compound rule-types](#compound-rule-types)):
-
-    * is-required
-    * is-excluded
-    * subclass-of
-    * direct-subclass-of
-    * not-subclass-of
-    * not-direct-subclass-of
-    * superclass-of
-    * direct-superclass-of
-    * not-superclass-of
-    * not-direct-superclass-of
-    * equivalent-to
-    * not-equivalent-to
-    * instance-of
-    * direct-instance-of
-    * not-instance-of
-
-* `<when-rule-type>` can be any of the above rule types (or a combination of -- see [Compound rule-types](#compound-rule-types)) _except_ `is-required` and `is-excluded`
-
-### Rule types
-
-* The following rule types are called _presence_ rule types. They place restrictions on whether a cell in a given column can have data or not, and may take a value of either `true` (equivalently: `t`, `yes`, `y`) or `false` (equivalently: `f`, `no`, `n`). If no truth value is supplied, `true` is assumed.
-
-    * is-required
-        * When set to `true` (implicitly or explicitly), this indicates that cells in this column should have data, possibly conditional upon an optional when-clause. E.g. `is-required (when <when-rule-type> <when-value>)` (see [When-clauses](#when-clauses))
-    * is-excluded
-        * When set to `true` (implicitly or explicitly), this indicates that cells in this column must be empty, possibly conditional upon an optional when-clause. E.g. `is-excluded (when <when-rule-type> <when-value>)` (see [When-clauses](#when-clauses))
-
-* The following rule types are called _query_ rule types. They involve queries to the reasoner. Consider the example rule: `<query-type> 'vaccine'`. Replacing `<query-type>` with each of the below results in the following corresponding reasoner queries:
-
-    * subclass-of
-        * queries the reasoner to verify that the class represented in the current cell is a subclass of the class 'vaccine'
-    * direct-subclass-of
-        * queries the reasoner to verify that the class represented in the current cell is a direct subclass of the class 'vaccine'
-    * not-subclass-of
-        * queries the reasoner to verify that the class represented in the current cell is **not** a subclass of the class 'vaccine'
-    * not-direct-subclass-of
-        * queries the reasoner to verify that the class represented in the current cell is **not** a direct subclass of the class 'vaccine'
-    * superclass-of
-        * queries the reasoner to verify that the class represented in the current cell is a superclass of the class 'vaccine'
-    * direct-superclass-of
-        * queries the reasoner to verify that the class represented in the current cell is a direct superclass of the class 'vaccine'
-    * not-superclass-of
-        * queries the reasoner to verify that the class represented in the current cell is **not** a superclass of the class 'vaccine'
-    * not-superclass-of
-        * queries the reasoner to verify that the class represented in the current cell is **not** a direct superclass of the class 'vaccine'
-    * equivalent-to
-        * queries the reasoner to verify that the class represented in the current cell is equivalent to the class 'vaccine'
-    * not-equivalent-to
-        * queries the reasoner to verify that the class represented in the current cell is **not** equivalent to the class 'vaccine'
-    * instance-of
-        * queries the reasoner to verify that the individual represented in the current cell is an instance of the class 'vaccine'
-    * direct-instance-of
-        * queries the reasoner to verify that the individual represented in the current cell is a direct instance of the class 'vaccine'
-    * not-instance-of
-        * queries the reasoner to verify that the individual represented in the current cell is **not** an instance of the class 'vaccine'
-
-#### Further notes on `<value>` and `<when-value>`
-
-* For the rule types: `is-required` and `is-excluded`, `<value>` is _optional_ and if not specified defaults to _true_.
-
-* For other rule types, `<value>` is _mandatory_ and must be in the form of a description logic (DL) expression query, in Manchester syntax.
-
-* `instance-of`, `direct-instance-of`, and `not-instance-of` may only be applied to named individuals.
-
-* `subclass-of`, `direct-subclass-of`, `not-subclass-of`, `superclass-of`, `direct-superclass-of`, `not-superclass-of`, and `equivalent-to` may be applied only to classes or general class expressions.
-
-* `<when-subject-expr>` must describe an individual, a class, or a generalised class expression and can be in the form of an `rdfs:label`, an IRI, an abbreviated IRI, a general DL expression, or a wildcard.
-
-### Substitution
-
-Special variables can be specified within `<value>`, `<when-value>`, and `<when-subject-expr>` clauses to indicate another entity described in the data. These two types of variables can be used interchangably.
-
-#### Column Names
-
-Column names of the form `{column name}` are used to indicate the entity described by the data in that column of a cell of a given row. E.g.:
-
-```
-is-required (when {exposure material reported} equivalent-to ('Dengue virus' or 'Dengue virus 2'))
-```
-
-requires data in the current cell whenever the class indicate in the "exposure material reported" column of the current row is either 'Dengue virus' or 'Dengue virus 2'.
-
-```
-subclass-of hasBasisIn in some {exposure material id} (when {exposure material reported} subclass-of ('Dengue virus' or 'Dengue virus 2'))
-```
-
-requires that, whenever the class indicated in "exposure material reported" of the current row is a subclass of the class consisting of the union of `'Dengue virus'` and `'Dengue virus 2'`, the data in the current cell must be a subclass of the set of classes that bear the relation `hasBasisIn` to the class indicated in column "exposure material id" of the same row.
-
-#### Wildcards
-
-Wildcards of the form `%n` are used to indicate the entity described by the data in the _nth_ cell of a given row. E.g.:
-
-```
-is-required (when %1 equivalent-to ('Dengue virus' or 'Dengue virus 2'))
-```
-
-requires data in the current cell whenever the class indicated in column 1 of the current row is either 'Dengue virus' or 'Dengue virus 2'.
-
-```
-subclass-of hasBasisIn in some %2 (when %1 subclass-of ('Dengue virus' or 'Dengue virus 2'))
-```
-
-requires that, whenever the class indicated in column 1 of the current row is a subclass of the class consisting of the union of `'Dengue virus'` and `'Dengue virus 2'`, the data in the current cell must be a subclass of the set of classes that bear the relation `hasBasisIn` to the class indicated in column 2 of the same row.
-
-### When-clauses
-
-The optional when-clause indicates that the rule given in the main clause should be validated only when the when-clause is satisfied. If multiple when-clauses are specified (separated by `'&'`, then each when-clause must evaluate to _true_ in order for the main validation rule to execute. E.g.:
-
-```
-direct-subclass-of %2 (when %5 superclass-of 'exposure process' & %2 superclass-of vaccine)
-```
-
-indicates that the validation rule `'direct-subclass-of %2'` should only be run against the current cell when both the cell in column 5 is a superclass of `'exposure process'` and the cell in column 2 is a superclass of `vaccine`.
-
-### Compound rule-types
-
-`<rule-type>` and `<when-rule-type>` can take the form: `rule-type-1|rule-type-2|rule-type-3|...`
-
-E.g.
-```
-subclass-of|equivalent-to %3 (when %4 subclass-of|equivalent-to %2)
-```
-
-requires that, whenever the contents of the cell in column 4 of the given row are either a subclass-of or equivalent-to the contents of the cell in column 2, then the contents of the current cell must be a subclass-of or equivalent-to the contents of the cell in column 3.
-
-## Error Messages
-
-### Column Out of Range Error
-
-When a wildcard is used as part of a rule, the column number indicated must not be greater than the number of columns that are in the table data provided. See: [Wildcards](#wildcards).
-
-### Incorrect Format Error
-
-The name of the file specified using the `--table` or `--rules` options must end in either `.csv` or `.tsv`.
-
-### Invalid Presence Rule Error
-
-A rule of the presence type must be in the form of a truth value. If this is ommitted it defaults to 'true'. For example, the following are valid: `is-required true`, `is-excluded`, `is-excluded false`. See: [Presence Types and Query Types](#presence-types-and-query-types).
-
-### Invalid Skip Row Error
-
-The value of the `--skip-row` option must be an integer.
-
-### Invalid When Type Error
-
-The indicated when rule type is not one of the rule types allowed in a when-clause. See: [Validation Rule Syntax](#validation-rule-syntax).
-
-### Malformed When Clause Error
-
-The indicated when-clause could not be parsed. See: [When-Clauses](#when-clauses).
-
-### Malformed Rule Error
-
-The indicated rule could not be parsed. See: [Validation Rule Syntax](#validation-rule-syntax).
-
-### Missing Headers Error
-
-The table for `--rules` must contain the following headers: table, column, validation. You may also include any number of other headers, although the contents of these columns will be ignored.
-
-### Multiple Rules Error
-
-Each distinct column within a table may only have one validation rule defined.
-
-### No Main Error
-
-When a when-clause is specified, a main clause must also be specified, with the latter being evaluated only when the when-clause is satisfied. See: [Validation Rule Syntax](#validation-rule-syntax).
-
-### Table Not Provided Error
-
-The name of a `.csv` or `.tsv` file containing the table data to validate must be supplied using the `--table` option of the `validate` command. E.g. `robot validate --input myontology.owl --table mytable.csv`.
-
-### Unrecognized Query Type Error
-
-The query type indicated is not one of the recognized query types. See: [Presence Types and Query Types](#presence-types-and-query-types).
-
-### Unrecognized Rule Type Error
-
-The rule type indicated is not one of the recognized rule types. See: [Validation Rule Syntax](#validation-rule-syntax).
diff --git a/robot-command/src/main/java/org/obolibrary/robot/CommandLineInterface.java b/robot-command/src/main/java/org/obolibrary/robot/CommandLineInterface.java
index 1b935c87..a26de993 100644
--- a/robot-command/src/main/java/org/obolibrary/robot/CommandLineInterface.java
+++ b/robot-command/src/main/java/org/obolibrary/robot/CommandLineInterface.java
@@ -42,7 +42,6 @@ public class CommandLineInterface {
     m.addCommand("report", new ReportCommand());
     m.addCommand("template", new TemplateCommand());
     m.addCommand("unmerge", new UnmergeCommand());
-    m.addCommand("validate", new ValidateCommand());
     m.addCommand("validate-profile", new ValidateProfileCommand());
     m.addCommand("verify", new VerifyCommand());
     return m;
diff --git a/robot-command/src/main/java/org/obolibrary/robot/ValidateCommand.java b/robot-command/src/main/java/org/obolibrary/robot/ValidateCommand.java
deleted file mode 100644
index c12086b6..00000000
--- a/robot-command/src/main/java/org/obolibrary/robot/ValidateCommand.java
+++ /dev/null
@@ -1,288 +0,0 @@
-package org.obolibrary.robot;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.Options;
-import org.semanticweb.owlapi.model.OWLOntology;
-import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Handles command-line options for the {@link ValidateOperation}.
- *
- * @author <a href="mailto:consulting@michaelcuffaro.com">Michael Cuffaro</a>
- */
-public class ValidateCommand implements Command {
-  /** Logger */
-  private static final Logger logger = LoggerFactory.getLogger(ValidateCommand.class);
-
-  /** Used to store the command-line options for the command. */
-  private Options options;
-
-  /** Namespace for error messages. */
-  private static final String NS = "validate#";
-
-  private static final String tableNotProvidedError =
-      NS + "TABLE NOT PROVIDED ERROR a table file must be specified to run this command";
-
-  private static final String incorrectFormatError =
-      NS + "INCORRECT FORMAT ERROR input file '%s' must end in either .csv or .tsv";
-
-  private static final String incorrectOutputFormatError =
-      NS + "INCORRECT OUTPUT FORMAT ERROR the output format must be one of HTML, XLSX, or TXT";
-
-  private static final String missingOutputDirectoryError =
-      NS + "MISSING OUTPUT DIRECTORY ERROR output directory required when format is specified";
-
-  private static final String invalidOutputDirectoryError =
-      NS
-          + "INVALID OUTPUT DIRECTORY ERROR the specified output directory does not exist or "
-          + "is not writable";
-
-  private static final String invalidSkipRowError =
-      NS + "INVALID SKIP ROW ERROR the specified skip-row must be an integer";
-
-  /** Constructor: Initialises the command with its various options. */
-  public ValidateCommand() {
-    Options o = CommandLineHelper.getCommonOptions();
-    o.addOption("t", "table", true, "file containing data (in CSV or TSV format) to validate");
-    o.addOption(
-        "T", "tables", true, "directory containing tables (in CSV or TSV format) to validate");
-    o.addOption("R", "rules", true, "file containing validation rules");
-    o.addOption("i", "input", true, "input file containing the ontology data to validate against");
-    o.addOption(
-        "f",
-        "format",
-        true,
-        "format for output file (XLSX, HTML, TXT) (if unspecified, output is sent to STDOUT)");
-    o.addOption(
-        "o",
-        "output-dir",
-        true,
-        "directory where output files will be saved (ignored if format option is left unspecified)");
-    o.addOption("k", "skip-row", true, "ignore the given row from the tables to be validated");
-    o.addOption(
-        "r",
-        "reasoner",
-        true,
-        "reasoner to use; must be one of: structural, hermit, jfact, emr, elk (default)");
-    o.addOption(
-        "s", "standalone", true, "If false, do not put HTML headers/script in the HTML output");
-    o.addOption("n", "no-fail", true, "If true, do not fail even if there are failed validations");
-    o.addOption("S", "silent", true, "If false, print all failed validations");
-    o.addOption(
-        "w",
-        "write-all",
-        true,
-        "If true, write all tables to output directory - including tables with no failed validations");
-    o.addOption("e", "errors", true, "Write errors-only for all tables to given path");
-    options = o;
-  }
-
-  /**
-   * Returns the name of the command
-   *
-   * @return name
-   */
-  public String getName() {
-    return "validate";
-  }
-
-  /**
-   * Returns a brief description of the command.
-   *
-   * @return description
-   */
-  public String getDescription() {
-    return "validate the data in the given file";
-  }
-
-  /**
-   * Returns the command-line usage for the command.
-   *
-   * @return usage
-   */
-  public String getUsage() {
-    return "validate --rules <file> --table <file> [--table <file> ...] [--skip-row k] --input <file> "
-        + "[--reasoner <name>] [--format (HTML|XLSX|TXT)] [--output-dir <directory>] "
-        + "[--standalone (true|false)] [--no-fail (true|false)] [--silent (true|false)]";
-  }
-
-  /**
-   * Returns the command-line options for the command.
-   *
-   * @return options
-   */
-  public Options getOptions() {
-    return options;
-  }
-
-  /**
-   * Handles the command-line and file operations for the ValidateOperation
-   *
-   * @param args strings to use as arguments
-   */
-  public void main(String[] args) {
-    try {
-      execute(null, args);
-    } catch (Exception e) {
-      CommandLineHelper.handleException(e);
-    }
-  }
-
-  /**
-   * Accepts an input state and command line arguments and calls ValidateOperation to validate the
-   * data that has been passed. Returns the final state of the command.
-   *
-   * @param state the state from the previous command, or null
-   * @param args the command-line arguments
-   * @return the input state, unchanged
-   * @throws Exception on any problem
-   */
-  public CommandState execute(CommandState state, String[] args) throws Exception {
-    CommandLine line = CommandLineHelper.getCommandLine(getUsage(), getOptions(), args);
-    if (line == null) {
-      return null;
-    }
-
-    if (state == null) {
-      state = new CommandState();
-    }
-
-    // Get the input ontology either from the command line directly or as part of the command
-    // chain:
-    IOHelper ioHelper = CommandLineHelper.getIOHelper(line);
-    state = CommandLineHelper.updateInputOntology(ioHelper, state, line);
-    OWLOntology ontology = state.getOntology();
-
-    // Get the reasoner specified by the user and if none is specified, use the default:
-    if (CommandLineHelper.getOptionalValue(line, "reasoner") == null) {
-      logger.info("No reasoner specified. Will use the default.");
-    }
-    OWLReasonerFactory reasonerFactory = CommandLineHelper.getReasonerFactory(line, true);
-
-    // Override default reasoner options with command-line options
-    Map<String, String> validateOptions = ValidateOperation.getDefaultOptions();
-    for (String option : validateOptions.keySet()) {
-      if (line.hasOption(option)) {
-        validateOptions.put(option, line.getOptionValue(option));
-      }
-    }
-
-    String outFormat = OptionsHelper.getOption(validateOptions, "format");
-    String outDir = OptionsHelper.getOption(validateOptions, "output-dir");
-
-    // If an output format has been specified, make sure that it is of a supported kind and that
-    // the output directory is valid. If it hasn't been specified it will just be passed as null to
-    // the validate operation.
-    if (outFormat != null) {
-      if (!(outFormat.equalsIgnoreCase("html")
-          || outFormat.equalsIgnoreCase("xlsx")
-          || outFormat.equalsIgnoreCase("txt"))) {
-        throw new IllegalArgumentException(incorrectOutputFormatError);
-      }
-
-      if (outDir == null) {
-        throw new IllegalArgumentException(missingOutputDirectoryError);
-      }
-
-      File d = new File(outDir);
-      if (!d.exists() || !d.isDirectory() || !d.canWrite()) {
-        throw new IllegalArgumentException(invalidOutputDirectoryError);
-      }
-    }
-
-    // Get the rules (table -> column -> rule)
-    String rulesPath =
-        CommandLineHelper.getRequiredValue(line, "rules", "A rules table must be provided");
-    if (!rulesPath.endsWith(".csv") && !rulesPath.endsWith(".tsv")) {
-      throw new IllegalArgumentException(String.format(incorrectFormatError, rulesPath));
-    }
-    Map<String, Map<String, String>> rules = ValidateOperation.getRules(rulesPath);
-
-    // Get the paths to the tables given in the --table or --tables arguments.
-    List<String> tablePaths = CommandLineHelper.getOptionalValues(line, "table");
-    String tableDir = CommandLineHelper.getOptionalValue(line, "tables");
-    if (tableDir != null) {
-      File dir = new File(tableDir);
-      if (!dir.isDirectory()) {
-        throw new IllegalArgumentException("The --tables value must be a valid directory");
-      }
-      for (File f : dir.listFiles()) {
-        tablePaths.add(f.getPath());
-      }
-    }
-    if (tablePaths.isEmpty()) {
-      throw new IllegalArgumentException(tableNotProvidedError);
-    }
-
-    // Extract all of the data from each of the given table paths. Only TSV and CSV tables
-    // are currently supported.
-    Map<String, List<List<String>>> tables = new HashMap<>();
-    for (String tablePath : tablePaths) {
-      List<List<String>> tableData;
-      if (tablePath.toLowerCase().endsWith(".tsv")) {
-        tableData = IOHelper.readTSV(tablePath);
-      } else if (tablePath.toLowerCase().endsWith(".csv")) {
-        tableData = IOHelper.readCSV(tablePath);
-      } else {
-        throw new IllegalArgumentException(String.format(incorrectFormatError, tablePath));
-      }
-
-      // If the `--skip-row` switch has been specified, then possibly delete the specified row from
-      // the table:
-      String rowToSkipStr;
-      if ((rowToSkipStr = CommandLineHelper.getOptionalValue(line, "skip-row")) != null) {
-        try {
-          int rowToSkip = Integer.parseInt(rowToSkipStr);
-          if (rowToSkip > tableData.size() || rowToSkip < 1) {
-            logger.warn(
-                "ignoring skip-row value: {}; there are {} rows in '{}'",
-                rowToSkip,
-                tableData.size(),
-                tablePath);
-          } else {
-            tableData.remove(rowToSkip - 1);
-          }
-        } catch (NumberFormatException e) {
-          throw new IllegalArgumentException(invalidSkipRowError);
-        }
-      }
-      tables.put(tablePath, tableData);
-    }
-
-    boolean noFail = CommandLineHelper.getBooleanValue(line, "no-fail", false);
-
-    // Finally send everything to the validate operation:
-    List<String> invalidTables =
-        ValidateOperation.validate(
-            rules, tables, ontology, ioHelper, reasonerFactory, validateOptions);
-
-    if (!invalidTables.isEmpty() && !noFail) {
-      // Print last error message - a summary of tables with errors
-      StringBuilder sb = new StringBuilder();
-      sb.append("VALIDATION FAILED - the following table(s) had one or more rule violation:");
-      for (String it : invalidTables) {
-        sb.append("\n- ").append(it);
-        if (outDir != null && outFormat != null) {
-          // If there is an output for this (not just printed to console)
-          // provide the path to the output file
-          sb.append(" (")
-              .append(new File(outDir).getPath())
-              .append("/")
-              .append(it.split("\\.")[0])
-              .append(".")
-              .append(outFormat.toLowerCase())
-              .append(")");
-        }
-      }
-      System.out.println(sb.toString());
-      System.exit(1);
-    }
-    return state;
-  }
-}
diff --git a/robot-core/src/main/java/org/obolibrary/robot/ExplainOperation.java b/robot-core/src/main/java/org/obolibrary/robot/ExplainOperation.java
index f1c1b3db..148ea8fc 100644
--- a/robot-core/src/main/java/org/obolibrary/robot/ExplainOperation.java
+++ b/robot-core/src/main/java/org/obolibrary/robot/ExplainOperation.java
@@ -3,7 +3,6 @@ package org.obolibrary.robot;
 import java.util.*;
 import java.util.stream.Collectors;
 import javax.annotation.Nonnull;
-
 import org.semanticweb.owl.explanation.api.*;
 import org.semanticweb.owl.explanation.impl.blackbox.checker.InconsistentOntologyExplanationGeneratorFactory;
 import org.semanticweb.owl.explanation.impl.rootderived.StructuralRootDerivedReasoner;
@@ -293,7 +292,12 @@ public class ExplainOperation {
     StringBuilder builder = new StringBuilder();
     builder.append("## Axioms used ").append(impact).append(" times").append("\n");
     for (OWLAxiom ax : axioms) {
-      builder.append("- ").append(renderer.render(ax)).append(" [").append(String.join(",", associatedOntologyIds.get(ax))).append("]\n");
+      builder
+          .append("- ")
+          .append(renderer.render(ax))
+          .append(" [")
+          .append(String.join(",", associatedOntologyIds.get(ax)))
+          .append("]\n");
     }
     builder.append("\n");
     return builder.toString();
@@ -313,11 +317,11 @@ public class ExplainOperation {
   }
 
   private static Set<OWLClass> getMostGeneralUnsatisfiableClasses(
-    OWLReasoner reasoner, OWLOntology ontology) {
+      OWLReasoner reasoner, OWLOntology ontology) {
     Set<OWLClass> mgu = new HashSet<>();
     OWLReasoner structural = new StructuralReasonerFactory().createReasoner(ontology);
     Set<OWLClass> unsats =
-      new HashSet<>(reasoner.getUnsatisfiableClasses().getEntitiesMinusBottom());
+        new HashSet<>(reasoner.getUnsatisfiableClasses().getEntitiesMinusBottom());
     for (OWLClass c : unsats) {
       Set<OWLClass> superclasses = structural.getSuperClasses(c, false).getFlattened();
       superclasses.retainAll(unsats);
diff --git a/robot-core/src/main/java/org/obolibrary/robot/TableValidator.java b/robot-core/src/main/java/org/obolibrary/robot/TableValidator.java
deleted file mode 100644
index f8caa36b..00000000
--- a/robot-core/src/main/java/org/obolibrary/robot/TableValidator.java
+++ /dev/null
@@ -1,1498 +0,0 @@
-package org.obolibrary.robot;
-
-import com.google.common.collect.Lists;
-import java.io.*;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import org.apache.commons.io.FilenameUtils;
-import org.apache.poi.ss.usermodel.FillPatternType;
-import org.apache.poi.ss.usermodel.IndexedColors;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.obolibrary.robot.export.*;
-import org.obolibrary.robot.providers.CURIEShortFormProvider;
-import org.obolibrary.robot.providers.QuotedAnnotationValueShortFormProvider;
-import org.semanticweb.owlapi.apibinding.OWLManager;
-import org.semanticweb.owlapi.io.OWLParserException;
-import org.semanticweb.owlapi.manchestersyntax.parser.ManchesterOWLSyntaxClassExpressionParser;
-import org.semanticweb.owlapi.model.*;
-import org.semanticweb.owlapi.reasoner.Node;
-import org.semanticweb.owlapi.reasoner.NodeSet;
-import org.semanticweb.owlapi.reasoner.OWLReasoner;
-import org.semanticweb.owlapi.util.ShortFormProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class TableValidator {
-
-  /** Logger */
-  private static final Logger logger = LoggerFactory.getLogger(ValidateOperation.class);
-
-  /** Namespace for error messages. */
-  private static final String NS = "validate#";
-
-  /** Error message for a rule that couldn't be parsed */
-  private static final String malformedRuleError = NS + "MALFORMED RULE ERROR malformed rule: %s";
-
-  /**
-   * Error message for an invalid presence rule. Presence rules must be in the form of a truth
-   * value.
-   */
-  private static final String invalidPresenceRuleError =
-      NS
-          + "INVALID PRESENCE RULE ERROR in column %d: invalid rule: \"%s\" for rule type: %s. Must be "
-          + "one of: true, t, 1, yes, y, false, f, 0, no, n";
-
-  /** Error message for invalid output format. */
-  private static final String invalidFormatError =
-      NS + "INVALID FORMAT ERROR '%s' must be one of: html, xlsx, or txt";
-
-  /**
-   * Error reported when a wildcard in a rule specifies a column greater than the number of columns
-   * in the table.
-   */
-  private static final String columnOutOfRangeError =
-      NS
-          + "COLUMN OUT OF RANGE ERROR in column %d: rule \"%s\" indicates a column number that is "
-          + "greater than the row length (%d).";
-
-  /** Error reported when a when-clause does not have a corresponding main clause */
-  private static final String noMainError =
-      NS + "NO MAIN ERROR in column %d: rule: \"%s\" has when clause but no main clause.";
-
-  /** Error reported when a when-clause can't be parsed */
-  private static final String malformedWhenClauseError =
-      NS + "MALFORMED WHEN CLAUSE ERROR in column %d: unable to decompose when-clause: \"%s\".";
-
-  /** Error reported when a when-clause is of an invalid or inappropriate type */
-  private static final String invalidWhenTypeError =
-      NS
-          + "INVALID WHEN TYPE ERROR in column %d: in clause: \"%s\": Only rules of type: %s are "
-          + "allowed in a when clause.";
-
-  /** Error reported when a query type is unrecognized */
-  private static final String unrecognizedQueryTypeError =
-      NS
-          + "UNRECOGNIZED QUERY TYPE ERROR in column %d: query type \"%s\" not recognized in rule "
-          + "\"%s\".";
-
-  /** Error reported when a rule type is not recognized */
-  private static final String unrecognizedRuleTypeError =
-      NS + "UNRECOGNIZED RULE TYPE ERROR in column %d: unrecognized rule type \"%s\".";
-
-  /** Reverse map from rule types (as Strings) to RTypeEnums, populated at load time */
-  private static final Map<String, RTypeEnum> rule_type_to_rtenum_map = new HashMap<>();
-
-  static {
-    for (RTypeEnum r : RTypeEnum.values()) {
-      rule_type_to_rtenum_map.put(r.getRuleType(), r);
-    }
-  }
-
-  /**
-   * Reverse map from rule types in the QUERY category (as Strings) to RTypeEnums, populated at load
-   * time
-   */
-  private static final Map<String, RTypeEnum> query_type_to_rtenum_map = new HashMap<>();
-
-  static {
-    for (RTypeEnum r : RTypeEnum.values()) {
-      if (r.getRuleCat() == RCatEnum.QUERY) {
-        query_type_to_rtenum_map.put(r.getRuleType(), r);
-      }
-    }
-  }
-
-  private OWLOntology ontology;
-  private String outFormat = null;
-  private String outDir;
-
-  /** The parser to use when validating class expressions */
-  private ManchesterOWLSyntaxClassExpressionParser parser;
-
-  private OWLReasoner reasoner;
-
-  private static final OWLDataFactory dataFactory = OWLManager.getOWLDataFactory();
-
-  private Map<IRI, String> iriToLabelMap;
-  private Map<String, IRI> labelToIRIMap;
-
-  private List<String> invalidTables = new ArrayList<>();
-  private List<String> messages = new ArrayList<>();
-
-  private Table outTable = null;
-  private String currentTable;
-  private int colNum;
-  private int rowIdx;
-  private int rowNum;
-  private boolean valid;
-  private boolean silent;
-
-  private List<String[]> errors = new ArrayList<>();
-
-  private Cell currentCell = null;
-
-  private ShortFormProvider provider;
-
-  /**
-   * Init a new TableValidator.
-   *
-   * @param ontology OWLOntology to use to validate table
-   * @param ioHelper IOHelper to resolve prefixes of CURIEs
-   * @param parser class expression parser to read Manchester class expressions from the table
-   * @param reasoner OWLReasoner to resolve validation queries
-   * @param outFormat output format of validation
-   * @param outDir output directory
-   */
-  public TableValidator(
-      OWLOntology ontology,
-      IOHelper ioHelper,
-      ManchesterOWLSyntaxClassExpressionParser parser,
-      OWLReasoner reasoner,
-      String outFormat,
-      String outDir) {
-    this.ontology = ontology;
-    this.parser = parser;
-    this.reasoner = reasoner;
-    if (outFormat != null) {
-      // Add the format and validate it
-      this.outFormat = outFormat.toLowerCase();
-      if (!Lists.newArrayList("xlsx", "html", "txt").contains(this.outFormat)) {
-        throw new IllegalArgumentException(String.format(invalidFormatError, outFormat));
-      }
-    }
-    this.outDir = outDir;
-
-    // Extract from the ontology two convenience maps from rdfs:labels to IRIs and vice versa:
-    iriToLabelMap = OntologyHelper.getLabels(ontology);
-    labelToIRIMap = reverseIRILabelMap(iriToLabelMap);
-
-    // Create some providers for rendering entities
-    ShortFormProvider oboProvider = new CURIEShortFormProvider(ioHelper.getPrefixes());
-    provider =
-        new QuotedAnnotationValueShortFormProvider(
-            ontology.getOWLOntologyManager(),
-            oboProvider,
-            ioHelper.getPrefixManager(),
-            Collections.singletonList(OWLManager.getOWLDataFactory().getRDFSLabel()),
-            Collections.emptyMap());
-
-    errors.add(new String[] {"table", "cell", "rule", "message"});
-  }
-
-  /** Turn logging on or off. */
-  public void toggleLogging() {
-    silent = !silent;
-  }
-
-  /**
-   * Get a list of errors from validate.
-   *
-   * @return list of errors
-   */
-  public List<String[]> getErrors() {
-    return errors;
-  }
-
-  /**
-   * Validate a set of tables.
-   *
-   * @param rules map of table name to column to rule
-   * @param tables tables to validate (map of table name to table contents)
-   * @param options map of validate options
-   * @return List of invalid tables (or empty list on success)
-   * @throws Exception on any problem
-   */
-  public List<String> validate(
-      Map<String, Map<String, String>> rules,
-      Map<String, List<List<String>>> tables,
-      Map<String, String> options)
-      throws Exception {
-
-    int skippedRow = Integer.parseInt(OptionsHelper.getOption(options, "skip-row", "0"));
-
-    // Validate all of the tables in turn:
-    for (Map.Entry<String, List<List<String>>> table : tables.entrySet()) {
-      // Reset valid for new table
-      valid = true;
-      outTable = new Table(outFormat);
-      String tablePath = table.getKey();
-      List<List<String>> data = table.getValue();
-
-      String tableName = FilenameUtils.getBaseName(FilenameUtils.getName(tablePath));
-      currentTable = tableName + "." + FilenameUtils.getExtension(tablePath);
-
-      // Get the header
-      List<String> headerRow = data.remove(0);
-
-      // Get the rules
-      Map<String, String> tableRules = rules.getOrDefault(tableName, null);
-      if (tableRules == null) {
-        // Skip this table if no rules
-        continue;
-      }
-
-      if (outFormat == null) {
-        System.out.println(String.format("Validating %s ...", currentTable));
-      }
-
-      int headerIdx = 0;
-      while (headerIdx < headerRow.size()) {
-        String header = headerRow.get(headerIdx);
-        String rawRule = tableRules.getOrDefault(header, null);
-        if (rawRule == null) {
-          // Skip this column if no rules
-          headerIdx++;
-          continue;
-        }
-        Column c = new Column(header, parseRules(rawRule), rawRule, provider);
-        outTable.addColumn(c);
-        headerIdx++;
-      }
-      List<Column> columns = outTable.getColumns();
-
-      // Get number to add to rowIdx to get true row number from input table
-      // as rowIdx starts at 0 and does not include header and rule rows
-      int addToRow;
-      if (skippedRow > 0 && skippedRow <= 2) {
-        // Skipped row is in header, add 1 to our reporting
-        addToRow = 3;
-      } else {
-        addToRow = 2;
-      }
-
-      // Validate data row by row, column by column
-      for (rowIdx = 0; rowIdx < data.size(); rowIdx++) {
-        rowNum = rowIdx + addToRow;
-        if (rowNum == skippedRow) {
-          // Skipped row occurs in the data
-          addToRow = 3;
-        }
-
-        List<String> row = data.get(rowIdx);
-        if (!hasContent(row)) {
-          logger.debug(String.format("Skipping empty row %d", rowNum));
-          continue;
-        }
-
-        Row outRow = null;
-        if (outFormat != null) {
-          outRow = new Row();
-        }
-
-        for (colNum = 0; colNum < columns.size(); colNum++) {
-          Column c = columns.get(colNum);
-          Map<String, List<String>> cRules = c.getRules();
-
-          // Get the contents of the current cell:
-          String cellString = colNum < row.size() ? row.get(colNum) : "";
-
-          // Extract all the data entries contained within the current cell:
-          List<String> cellData = Lists.newArrayList(cellString.trim().split("\\|"));
-
-          // Create the cell object
-          currentCell = getCell(c, cellData);
-
-          if (cRules == null || cRules.isEmpty()) {
-            // No rules to validate, just add the cell exactly as is
-            if (outRow != null) {
-              outRow.add(currentCell);
-            }
-            continue;
-          }
-
-          // For each of the rules applicable to this column, validate each entry in the cell
-          // against it:
-          for (Map.Entry<String, List<String>> ruleEntry : cRules.entrySet()) {
-            for (String rule : ruleEntry.getValue()) {
-              List<String> interpolatedRules = interpolateRule(rule, headerRow, row);
-              for (String interpolatedRule : interpolatedRules) {
-                for (String d : cellData) {
-                  String errorMsg = validateRule(d, interpolatedRule, row, ruleEntry.getKey());
-                  if (errorMsg != null) {
-                    // An error was returned, add to errors
-                    errors.add(
-                        new String[] {
-                          tableName,
-                          IOHelper.cellToA1(rowNum, colNum + 1),
-                          ruleEntry.getKey(),
-                          errorMsg,
-                        });
-                  }
-                }
-              }
-            }
-          }
-          if (outRow != null) {
-            outRow.add(currentCell);
-          }
-        }
-        if (outFormat != null) {
-          outTable.addRow(outRow);
-        }
-      }
-
-      if (!valid) {
-        invalidTables.add(currentTable);
-      }
-
-      boolean standalone = OptionsHelper.optionIsTrue(options, "standalone");
-      boolean writeAll = OptionsHelper.optionIsTrue(options, "write-all");
-
-      // Write table if: write-all is true OR table is not valid (for non-null formats)
-      if ((writeAll || !valid) && outFormat != null) {
-        String outPath =
-            outDir + "/" + FilenameUtils.getBaseName(tablePath) + "." + outFormat.toLowerCase();
-        switch (outFormat.toLowerCase()) {
-          case "xlsx":
-            try (Workbook wb = outTable.asWorkbook("|");
-                FileOutputStream fos = new FileOutputStream(outPath)) {
-              wb.write(fos);
-            }
-            break;
-          case "html":
-            try (PrintWriter out = new PrintWriter(outPath)) {
-              out.print(outTable.toHTML("|", standalone, true));
-            }
-            break;
-          case "txt":
-            try (PrintWriter out = new PrintWriter(outPath)) {
-              for (String m : messages) {
-                out.println(m);
-              }
-            }
-            break;
-        }
-      }
-    }
-    return invalidTables;
-  }
-
-  /**
-   * Given a map from IRIs to strings, return its inverse.
-   *
-   * @param source source map
-   * @return inverse map
-   */
-  private static Map<String, IRI> reverseIRILabelMap(Map<IRI, String> source) {
-    HashMap<String, IRI> target = new HashMap<>();
-    for (Map.Entry<IRI, String> entry : source.entrySet()) {
-      String reverseKey = entry.getValue();
-      IRI reverseValue = entry.getKey();
-      if (target.containsKey(reverseKey)) {
-        logger.warn(
-            "Duplicate rdfs:label \"{}\". Overwriting value \"{}\" with \"{}\"",
-            reverseKey,
-            target.get(reverseKey),
-            reverseValue);
-      }
-      target.put(reverseKey, reverseValue);
-    }
-    return target;
-  }
-
-  /**
-   * Given an OWLClass describing a subject class from the ontology, an OWLClassExpression
-   * describing a rule to query that subject class against, a string representing the query types to
-   * use when evaluating the results of the query, and a list of strings describing a row from the
-   * CSV: Determine whether, for any of the given query types, the given subject is in the result
-   * set returned by the reasoner for that query type. Return true if it is in at least one of these
-   * result sets, and false if it is not.
-   *
-   * @param subjectClass subject from input ontology
-   * @param ruleCE OWLClassExpression interpretation of the rule
-   * @param row row being validated
-   * @param unsplitQueryType query types
-   * @return true if subject is in at least one result set
-   * @throws Exception on unknown query type
-   */
-  private boolean executeClassQuery(
-      OWLClass subjectClass, OWLClassExpression ruleCE, List<String> row, String unsplitQueryType)
-      throws Exception {
-
-    logger.debug(
-        String.format(
-            "execute_class_query(): Called with parameters: "
-                + "subjectClass: \"%s\", "
-                + "ruleCE: \"%s\", "
-                + "row: \"%s\", "
-                + "query type: \"%s\".",
-            subjectClass, ruleCE, row, unsplitQueryType));
-
-    // For each of the query types associated with the rule, check to see if the rule is satisfied
-    // thus interpreted. If it is, then we return true, since multiple query types are interpreted
-    // as a disjunction. If a query type is unrecognized, inform the user but continue on.
-    String[] queryTypes = unsplitQueryType.split("\\|");
-    for (String queryType : queryTypes) {
-      if (unknownRuleType(queryType)) {
-        throw new Exception(
-            String.format(unrecognizedQueryTypeError, colNum + 1, queryType, unsplitQueryType));
-      }
-
-      RTypeEnum qType = query_type_to_rtenum_map.get(queryType);
-      if (qType == RTypeEnum.SUB
-          || qType == RTypeEnum.DIRECT_SUB
-          || qType == RTypeEnum.NOT_SUB
-          || qType == RTypeEnum.NOT_DIRECT_SUB) {
-        // Check to see if the subjectClass is a (direct) subclass of the given rule:
-        // Get direct and not bools
-        boolean direct = false;
-        if (qType == RTypeEnum.DIRECT_SUB || qType == RTypeEnum.NOT_DIRECT_SUB) {
-          direct = true;
-        }
-        boolean not = false;
-        if (qType == RTypeEnum.NOT_SUB || qType == RTypeEnum.NOT_DIRECT_SUB) {
-          not = true;
-        }
-        NodeSet<OWLClass> subClassesFound = reasoner.getSubClasses(ruleCE, direct);
-        if (not && !subClassesFound.containsEntity(subjectClass)
-            || !not && subClassesFound.containsEntity(subjectClass)) {
-          // NOT and not in set OR in set
-          return true;
-        }
-
-      } else if (qType == RTypeEnum.SUPER
-          || qType == RTypeEnum.DIRECT_SUPER
-          || qType == RTypeEnum.NOT_SUPER
-          || qType == RTypeEnum.NOT_DIRECT_SUPER) {
-        // Check to see if the subjectClass is a (direct) superclass of the given rule:
-        // Get direct and not bools
-        boolean direct = false;
-        if (qType == RTypeEnum.DIRECT_SUPER || qType == RTypeEnum.NOT_DIRECT_SUPER) {
-          direct = true;
-        }
-        boolean not = false;
-        if (qType == RTypeEnum.NOT_SUPER || qType == RTypeEnum.NOT_DIRECT_SUPER) {
-          not = true;
-        }
-
-        NodeSet<OWLClass> superClassesFound = reasoner.getSuperClasses(ruleCE, direct);
-        if (not && !superClassesFound.containsEntity(subjectClass)
-            || !not && superClassesFound.containsEntity(subjectClass)) {
-          // NOT and not in set OR in set
-          return true;
-        }
-
-      } else if (qType == RTypeEnum.EQUIV || qType == RTypeEnum.NOT_EQUIV) {
-        // Check to see if the subjectClass is an equivalent of the given rule:
-        boolean not = false;
-        if (qType == RTypeEnum.NOT_EQUIV) {
-          not = true;
-        }
-        Node<OWLClass> equivClassesFound = reasoner.getEquivalentClasses(ruleCE);
-        if (!not && equivClassesFound.contains(subjectClass)
-            || not && !equivClassesFound.contains(subjectClass)) {
-          return true;
-        }
-
-      } else {
-        // Spit out an error in this case but continue validating the other rules:
-        logger.error(
-            String.format(
-                "%s validation not possible for OWLClass %s.", qType.getRuleType(), subjectClass));
-      }
-    }
-    return false;
-  }
-
-  /**
-   * Given an OWLClassExpression describing an unnamed subject class from the ontology, an
-   * OWLClassExpression describing a rule to query that subject class against, a string representing
-   * the query types to use when evaluating the results of the query, and a list of strings
-   * describing a row from the CSV: Determine whether, for any of the given query types, the given
-   * subject is in the result set returned by the reasoner for that query type. Return true if it is
-   * in at least one of these result sets, and false if it is not.
-   *
-   * @param subjectCE subject class expression from input ontology
-   * @param ruleCE OWLClassExpression interpretation of the rule
-   * @param row row being validated
-   * @param unsplitQueryType query types
-   * @return true if subject is in at least one result set
-   * @throws Exception on unknown query type
-   */
-  private boolean executeGeneralizedClassQuery(
-      OWLClassExpression subjectCE,
-      OWLClassExpression ruleCE,
-      List<String> row,
-      String unsplitQueryType)
-      throws Exception {
-
-    logger.debug(
-        String.format(
-            "execute_generalized_class_query(): Called with parameters: "
-                + "subjectCE: \"%s\", "
-                + "ruleCE: \"%s\", "
-                + "row: \"%s\", "
-                + "query type: \"%s\".",
-            subjectCE, ruleCE, row, unsplitQueryType));
-
-    // For each of the query types associated with the rule, check to see if the rule is satisfied
-    // thus interpreted. If it is, then we return true, since multiple query types are interpreted
-    // as a disjunction. If a query type is unrecognized, inform the user but continue on.
-    String[] queryTypes = unsplitQueryType.split("\\|");
-    for (String queryType : queryTypes) {
-      if (unknownRuleType(queryType)) {
-        throw new Exception(
-            String.format(unrecognizedQueryTypeError, colNum + 1, queryType, unsplitQueryType));
-      }
-
-      RTypeEnum qType = query_type_to_rtenum_map.get(queryType);
-      if (qType == RTypeEnum.SUB) {
-        // Check to see if the subjectClass is a subclass of the given rule:
-        OWLSubClassOfAxiom axiom = dataFactory.getOWLSubClassOfAxiom(subjectCE, ruleCE);
-        if (reasoner.isEntailed(axiom)) {
-          return true;
-        }
-      } else if (qType == RTypeEnum.NOT_SUB) {
-        // Check to see if the subjectClass is a subclass of the given rule:
-        OWLSubClassOfAxiom axiom = dataFactory.getOWLSubClassOfAxiom(subjectCE, ruleCE);
-        if (!reasoner.isEntailed(axiom)) {
-          return true;
-        }
-      } else if (qType == RTypeEnum.SUPER) {
-        // Check to see if the subjectClass is a superclass of the given rule:
-        OWLSubClassOfAxiom axiom = dataFactory.getOWLSubClassOfAxiom(ruleCE, subjectCE);
-        if (reasoner.isEntailed(axiom)) {
-          return true;
-        }
-      } else if (qType == RTypeEnum.NOT_SUPER) {
-        // Check to see if the subjectClass is a superclass of the given rule:
-        OWLSubClassOfAxiom axiom = dataFactory.getOWLSubClassOfAxiom(ruleCE, subjectCE);
-        if (!reasoner.isEntailed(axiom)) {
-          return true;
-        }
-      } else if (qType == RTypeEnum.EQUIV) {
-        OWLEquivalentClassesAxiom axiom =
-            dataFactory.getOWLEquivalentClassesAxiom(subjectCE, ruleCE);
-        if (reasoner.isEntailed(axiom)) {
-          return true;
-        }
-      } else if (qType == RTypeEnum.NOT_EQUIV) {
-        OWLEquivalentClassesAxiom axiom =
-            dataFactory.getOWLEquivalentClassesAxiom(subjectCE, ruleCE);
-        if (!reasoner.isEntailed(axiom)) {
-          return true;
-        }
-      } else {
-        // Spit out an error in this case but continue validating the other rules:
-        logger.error(
-            String.format(
-                "%s validation not possible for OWLClassExpression %s.",
-                qType.getRuleType(), subjectCE));
-      }
-    }
-    return false;
-  }
-
-  /**
-   * Given an OWLNamedIndividual describing a subject individual from the ontology, an
-   * OWLClassExpression describing a rule to query that subject individual against, a string
-   * representing the query types to use when evaluating the results of the query, and a list of
-   * strings describing a row from the CSV: Determine whether, for any of the given query types, the
-   * given subject is in the result set returned by the reasoner for that query type. Return true if
-   * it is in at least one of these result sets, and false if it is not.
-   *
-   * @param subjectIndividual individual from input ontology
-   * @param ruleCE OWLClassExpression interpretation of the rule
-   * @param row row being validated
-   * @param unsplitQueryType query types
-   * @return true if subject is in at least one result set
-   * @throws Exception on unknown query type
-   */
-  private boolean executeIndividualQuery(
-      OWLNamedIndividual subjectIndividual,
-      OWLClassExpression ruleCE,
-      List<String> row,
-      String unsplitQueryType)
-      throws Exception {
-
-    logger.debug(
-        String.format(
-            "execute_individual_query(): Called with parameters: "
-                + "subjectIndividual: \"%s\", "
-                + "ruleCE: \"%s\", "
-                + "row: \"%s\", "
-                + "query type: \"%s\".",
-            subjectIndividual, ruleCE, row, unsplitQueryType));
-
-    // For each of the query types associated with the rule, check to see if the rule is satisfied
-    // thus interpreted. If it is, then we return true, since multiple query types are interpreted
-    // as a disjunction. If a query type is unrecognized or not applicable to an individual, inform
-    // the user but continue on.
-    String[] queryTypes = unsplitQueryType.split("\\|");
-    for (String queryType : queryTypes) {
-      if (unknownRuleType(queryType)) {
-        throw new Exception(
-            String.format(unrecognizedQueryTypeError, colNum + 1, queryType, unsplitQueryType));
-      }
-
-      RTypeEnum qType = query_type_to_rtenum_map.get(queryType);
-      if (qType == RTypeEnum.INSTANCE
-          || qType == RTypeEnum.DIRECT_INSTANCE
-          || qType == RTypeEnum.NOT_INSTANCE) {
-        boolean not = false;
-        if (qType == RTypeEnum.NOT_INSTANCE) {
-          not = true;
-        }
-        NodeSet<OWLNamedIndividual> instancesFound =
-            reasoner.getInstances(ruleCE, qType == RTypeEnum.DIRECT_INSTANCE);
-        if (not && !instancesFound.containsEntity(subjectIndividual)
-            || !not && instancesFound.containsEntity(subjectIndividual)) {
-          return true;
-        }
-      } else {
-        // Spit out an error in this case but continue validating the other rules:
-        logger.error(
-            String.format(
-                "%s validation not possible for OWLNamedIndividual %s.",
-                qType.getRuleType(), subjectIndividual));
-      }
-    }
-    return false;
-  }
-
-  /**
-   * Given a string describing a subject term, a string describing a rule to query that subject term
-   * against, a string representing the query types to use when evaluating the results of the query,
-   * and a list of strings describing a row from the CSV: Determine whether, for any of the given
-   * query types, the given subject is in the result set returned by the reasoner for that query
-   * type. Return true if it is in at least one of these result sets, and false if it is not.
-   *
-   * @param subject subject term from the input ontology
-   * @param rule rule to use to validate
-   * @param row current row to validate
-   * @param unsplitQueryType query types
-   * @return true if subject is in at least one result set
-   * @throws Exception on unknown query type
-   */
-  private boolean executeQuery(
-      String subject, String rule, List<String> row, String unsplitQueryType) throws Exception {
-    logger.debug(
-        String.format(
-            "execute_query(): Called with parameters: "
-                + "subject: \"%s\", "
-                + "rule: \"%s\", "
-                + "row: \"%s\", "
-                + "query type: \"%s\".",
-            subject, rule, row, unsplitQueryType));
-
-    // Get the class expression corresponding to the rule that has been passed:
-    OWLClassExpression ruleCE = getClassExpression(rule);
-    if (ruleCE == null) {
-      report(
-          String.format(
-              "Unable to parse rule \"%s %s\" at column %d.", unsplitQueryType, rule, colNum + 1));
-      return false;
-    }
-
-    // Try to extract the label corresponding to the subject term:
-    String subjectLabel = getLabelFromTerm(subject);
-    if (subjectLabel != null) {
-      // Figure out if it is an instance or a class and run the appropriate query
-      IRI subjectIri = labelToIRIMap.get(subjectLabel);
-      OWLEntity subjectEntity = OntologyHelper.getEntity(ontology, subjectIri);
-      try {
-        OWLNamedIndividual subjectIndividual = subjectEntity.asOWLNamedIndividual();
-        return executeIndividualQuery(subjectIndividual, ruleCE, row, unsplitQueryType);
-      } catch (OWLRuntimeException e) {
-        try {
-          OWLClass subjectClass = subjectEntity.asOWLClass();
-          return executeClassQuery(subjectClass, ruleCE, row, unsplitQueryType);
-        } catch (OWLRuntimeException ee) {
-          // This actually should not happen, since if the subject has a label it should either
-          // be a named class or a named individual:
-          logger.error(
-              String.format(
-                  "While validating \"%s\" against \"%s %s\", encountered: %s",
-                  subject, unsplitQueryType, rule, ee));
-          return false;
-        }
-      }
-    } else {
-      // If no label corresponding to the subject term can be found, then try and parse it as a
-      // class expression and run a generalised query on it:
-      OWLClassExpression subjectCE = getClassExpression(subject);
-      if (subjectCE == null) {
-        logger.error(String.format("Unable to parse subject \"%s\" at row %d.", subject, rowNum));
-        return false;
-      }
-
-      try {
-        return executeGeneralizedClassQuery(subjectCE, ruleCE, row, unsplitQueryType);
-      } catch (UnsupportedOperationException e) {
-        logger.error("Generalized class expression queries are not supported by this reasoner.");
-        return false;
-      }
-    }
-  }
-
-  /**
-   * Given a string describing a term from the ontology, parse it into a class expression expressed
-   * in terms of the ontology. If the parsing fails, write a warning statement to the log.
-   *
-   * @param term Text to parse class expression from
-   * @return class expression
-   */
-  private OWLClassExpression getClassExpression(String term) {
-    OWLClassExpression ce;
-    try {
-      ce = parser.parse(term);
-    } catch (OWLParserException e) {
-      // If the parsing fails the first time, try surrounding the term in single quotes:
-      try {
-        ce = parser.parse("'" + term + "'");
-      } catch (OWLParserException ee) {
-        logger.warn(
-            String.format(
-                "Could not determine class expression from \"%s\".\n\t%s.",
-                term, e.getMessage().trim()));
-        return null;
-      }
-    }
-    return ce;
-  }
-
-  /**
-   * Create a Cell object based on cell data from the input table.
-   *
-   * @param column Column that this Cell will go into
-   * @param cellData list of strings from the cell
-   * @return Cell object for output Table
-   */
-  private Cell getCell(Column column, List<String> cellData) {
-    if (outFormat == null) {
-      return new Cell(column, cellData);
-    }
-
-    RendererType displayRenderer = outTable.getDisplayRendererType();
-    RendererType sortRenderer = outTable.getSortRendererType();
-    ShortFormProvider provider = column.getShortFormProvider();
-
-    List<String> display = new ArrayList<>();
-    List<String> sort = new ArrayList<>();
-
-    for (String val : cellData) {
-      // Try to get IRI based on label
-      IRI iri = labelToIRIMap.getOrDefault(val, null);
-      OWLClassExpression expr = null;
-      if (iri == null) {
-        // Try to use parser as a backup if we couldn't get the IRI
-        // e.g., if value provided was a CURIE or other short form
-        try {
-          expr = parser.parse(val);
-        } catch (Exception e) {
-          // Do nothing
-        }
-        if (expr == null) {
-          // Not a class expression
-          display.add(val);
-          sort.add(val);
-          continue;
-        }
-        if (!expr.isAnonymous()) {
-          iri = expr.asOWLClass().getIRI();
-        }
-      }
-
-      if (iri != null) {
-        // Maybe add HTML link
-        if (outFormat.equalsIgnoreCase("html")) {
-          display.add(String.format("<a href=\"%s\">%s</a>", iri.toString(), val));
-        } else {
-          display.add(val);
-        }
-        sort.add(val);
-      } else {
-        // No IRI, the expression is anonymous
-        // Render based on display/sort renderers and provider
-        display.add(OntologyHelper.renderManchester(expr, provider, displayRenderer));
-        sort.add(OntologyHelper.renderManchester(expr, provider, sortRenderer));
-      }
-    }
-    return new Cell(column, display, sort);
-  }
-
-  /**
-   * Given a string describing one of the classes in the ontology, in either the form of an IRI, an
-   * abbreviated IRI, or an rdfs:label, return the rdfs:label for that class.
-   *
-   * @param term text to get label from
-   * @return label or null
-   */
-  private String getLabelFromTerm(String term) {
-    if (term == null) {
-      return null;
-    }
-
-    // Remove any surrounding single quotes from the term:
-    term = term.replaceAll("^\'|\'$", "");
-
-    // If the term is already a recognized label, then just send it back:
-    if (iriToLabelMap.containsValue(term)) {
-      return term;
-    }
-
-    // Check to see if the term is a recognized IRI (possibly in short form), and if so return its
-    // corresponding label:
-    // TODO - short form might not work for everything, rework this
-    for (IRI iri : iriToLabelMap.keySet()) {
-      if (iri.toString().equals(term) || iri.getShortForm().equals(term)) {
-        return iriToLabelMap.get(iri);
-      }
-    }
-
-    // If the label isn't recognized, just return null:
-    return null;
-  }
-
-  /**
-   * Given a string in the form of a wildcard, and a list of strings representing a row of the CSV,
-   * return the rdfs:label contained in the position of the row indicated by the wildcard.
-   *
-   * @param wildcard wildcard in %d format
-   * @param row row to get contents from
-   * @return contents at the wildcard location
-   * @throws Exception on issue retrieving contents at wildcard location
-   */
-  private String getWildcardContents(String wildcard, List<String> row) throws Exception {
-    if (!wildcard.startsWith("%")) {
-      logger.error(String.format("Invalid wildcard: \"%s\".", wildcard));
-      return null;
-    }
-
-    int colIndex = Integer.parseInt(wildcard.substring(1)) - 1;
-    if (colIndex >= row.size()) {
-      throw new Exception(String.format(columnOutOfRangeError, colNum + 1, wildcard, row.size()));
-    }
-
-    String term = row.get(colIndex);
-    if (term == null || term.trim().equals("")) {
-      logger.info(
-          String.format(
-              "Failed to retrieve label from wildcard: %s. No term at position %d of this row.",
-              wildcard, colIndex + 1));
-      return null;
-    }
-
-    return term.trim();
-  }
-
-  /**
-   * Given a string specifying a list of rules of various types, return a map which contains, for
-   * each rule type present in the string, the list of rules of that type that have been specified.
-   *
-   * @param ruleString unparsed rule string
-   * @return map of parsed rule
-   * @throws Exception on malformed rule
-   */
-  private Map<String, List<String>> parseRules(String ruleString) throws Exception {
-    HashMap<String, List<String>> ruleMap = new HashMap<>();
-    // Skip over empty strings and strings that start with "##".
-    if (!ruleString.trim().equals("") && !ruleString.trim().startsWith("##")) {
-      // Rules are separated by semicolons:
-      String[] rules = ruleString.split("\\s*;\\s*");
-      for (String rule : rules) {
-        // Skip any rules that begin with a '#' (these are interpreted as commented out):
-        if (rule.trim().startsWith("#")) {
-          continue;
-        }
-        // Each rule is of the form: <rule-type> <rule-content> but for the PRESENCE category, if
-        // <rule-content> is left out it is implicitly understood to be "true"
-        String[] ruleParts = rule.trim().split("\\s+", 2);
-        String ruleType = ruleParts[0].trim();
-        String ruleContent;
-        if (ruleParts.length == 2) {
-          ruleContent = ruleParts[1].trim();
-        } else {
-          RTypeEnum rTypeEnum = rule_type_to_rtenum_map.get(ruleType);
-          if (rTypeEnum != null && rTypeEnum.getRuleCat() == RCatEnum.PRESENCE) {
-            ruleContent = "true";
-          } else {
-            throw new Exception(String.format(malformedRuleError, rule.trim()));
-          }
-        }
-
-        // Add, to the map, a new empty list for the given ruleType if we haven't seen it before:
-        if (!ruleMap.containsKey(ruleType)) {
-          ruleMap.put(ruleType, new ArrayList<>());
-        }
-        // Add the content of the given rule to the list of rules corresponding to its ruleType:
-        ruleMap.get(ruleType).add(ruleContent);
-      }
-    }
-    return ruleMap;
-  }
-
-  /**
-   * Given a list of strings representing a row from the table, return true if any of the cells in
-   * the row has non-whitespace content.
-   *
-   * @param row row to check
-   * @return true if row has contents
-   */
-  private boolean hasContent(List<String> row) {
-    for (String cell : row) {
-      if (!cell.trim().equals("")) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  /**
-   * Return a list of strings in which all of the column names in the input string have been
-   * replaced by the rdfs:labels corresponding to the content in the column of the row that they
-   * indicate.
-   *
-   * @param columnNameMap map of column name to contents (rdfs:label)
-   * @param interpolatedRules list of already-interpolated rules (potentially empty)
-   * @param rule the rule to interpolate
-   * @return list of interpolated rules (only one if no multi-value cells)
-   */
-  private List<String> interpolateColumnNames(
-      Map<String, String[]> columnNameMap, List<String> interpolatedRules, String rule) {
-    // Interpolate the rules using the column name map. If any of the column names point to a cell
-    // with multiple terms, then we duplicate the rule for each term pointed to. Finally we return
-    // all of
-    // the rules generated.
-    for (String colName : columnNameMap.keySet()) {
-      if (interpolatedRules.isEmpty()) {
-        // If we haven't yet interpolated anything, then base the current interpolation on the rule
-        // that has been passed as an argument to the function above, and generate an interpolated
-        // rule corresponding to every term corresponding to this key in the column name map.
-        for (String term : columnNameMap.get(colName)) {
-          if (term != null && !term.trim().equals("")) {
-            String label = getLabelFromTerm(term);
-            String interpolatedRule =
-                rule.replaceAll(
-                    String.format("\\{%s}", colName),
-                    label == null ? "(" + term + ")" : "'" + label + "'");
-            interpolatedRules.add(interpolatedRule);
-          }
-        }
-      } else {
-        // If we have already interpolated some rules, then every string that has been interpolated
-        // thus far must be interpolated again for every term corresponding to this key in the
-        // column name map, and the list of interpolated rules is then replaced with the new list.
-        List<String> tmpList = new ArrayList<>();
-        for (String term : columnNameMap.get(colName)) {
-          if (term != null && !term.trim().equals("")) {
-            String label = getLabelFromTerm(term);
-            for (String intStr : interpolatedRules) {
-              String interpolatedRule =
-                  intStr.replaceAll(
-                      String.format("\\{%s}", colName),
-                      label == null ? "(" + term + ")" : "'" + label + "'");
-              tmpList.add(interpolatedRule);
-            }
-          }
-        }
-        interpolatedRules = tmpList;
-      }
-    }
-    return interpolatedRules;
-  }
-
-  /**
-   * Return a list of strings in which all of the wildcards in the input string have been replaced
-   * by the rdfs:labels corresponding to the content in the positions of the row that they indicate.
-   *
-   * @param wildCardMap map of column number to contents (rdfs:label)
-   * @param interpolatedRules list of already-interpolated rules (potentially empty)
-   * @param rule the rule to interpolate
-   * @return list of interpolated rules (only one if no multi-value cells)
-   */
-  private List<String> interpolateWildcards(
-      Map<Integer, String[]> wildCardMap, List<String> interpolatedRules, String rule) {
-    // Now interpolate the rule using the wildcard map. If any of the wildcards points to a cell
-    // with multiple terms, then we duplicate the rule for each term pointed to. Finally we return
-    // all of the rules generated.
-    for (int i : wildCardMap.keySet()) {
-      if (interpolatedRules.isEmpty()) {
-        // If we haven't yet interpolated anything, then base the current interpolation on the rule
-        // that has been passed as an argument to the function above, and generate an interpolated
-        // rule corresponding to every term corresponding to this key in the wildcard map.
-        for (String term : wildCardMap.get(i)) {
-          if (term != null) {
-            String label = getLabelFromTerm(term);
-            String interpolatedRule =
-                rule.replaceAll(
-                    String.format("%%%d", i), label == null ? "(" + term + ")" : "'" + label + "'");
-            interpolatedRules.add(interpolatedRule);
-          }
-        }
-      } else {
-        // If we have already interpolated some rules, then every string that has been interpolated
-        // thus far must be interpolated again for every term corresponding to this key in the
-        // wildcard map, and the list of interpolated rules is then replaced with the new list.
-        List<String> tmpList = new ArrayList<>();
-        for (String term : wildCardMap.get(i)) {
-          if (term != null) {
-            String label = getLabelFromTerm(term);
-            for (String intStr : interpolatedRules) {
-              String interpolatedRule =
-                  intStr.replaceAll(
-                      String.format("%%%d", i),
-                      label == null ? "(" + term + ")" : "'" + label + "'");
-              tmpList.add(interpolatedRule);
-            }
-          }
-        }
-        interpolatedRules = tmpList;
-      }
-    }
-    return interpolatedRules;
-  }
-
-  /**
-   * Given a string, possibly containing wildcards, and a list of strings representing a row of the
-   * CSV, return a string in which all of the wildcards in the input string have been replaced by
-   * the rdfs:labels corresponding to the content in the positions of the row that they indicate.
-   *
-   * @param rule the rule to interpolate
-   * @param headers headers of this table
-   * @param row current row as list of strings
-   * @return list of interpolated rules (only one if no multi-value cells)
-   * @throws Exception on issue interpolating
-   */
-  private List<String> interpolateRule(String rule, List<String> headers, List<String> row)
-      throws Exception {
-    // This is what will be returned:
-    List<String> interpolatedRules = new ArrayList<>();
-
-    // If the rule only has whitespace in it, return an empty string back to the caller:
-    if (rule.trim().equals("")) {
-      interpolatedRules.add("");
-      return interpolatedRules;
-    }
-
-    // Look for column names within the given rule. These will be of the form {str} where str is the
-    // column name of the cell that is being pointed to. Then create a map from column name to the
-    // cell contents.
-    Matcher m = Pattern.compile("\\{([^}]+)}").matcher(rule);
-    Map<String, String[]> columnNameMap = new HashMap<>();
-    while (m.find()) {
-      String colName = m.group(1);
-      if (!columnNameMap.containsKey(colName)) {
-        if (!headers.contains(colName)) {
-          // TODO - exception
-          throw new Exception("Missing column in " + currentTable + ": " + colName);
-        }
-        int colIdx = headers.indexOf(colName);
-        String cellContents = row.get(colIdx);
-
-        // Either put the full cell contents or the split cell contents into the array
-        String[] terms = cellContents != null ? cellContents.split("\\|") : new String[] {null};
-        columnNameMap.put(colName, terms);
-      }
-    }
-
-    // Look for wildcards within the given rule. These will be of the form %d where d is the number
-    // of the cell the wildcard is pointing to (e.g. %1 is the first cell). Then create a map from
-    // wildcard numbers to the terms that they point to, which we extract from the cell
-    // indicated by the wildcard number. In general the terms will be split by pipes within a
-    // cell. E.g. if the wildcard is %1 and the first cell contains 'term1|term2|term3' then add an
-    // entry to the wildcard map like: 1 -> ['term1', 'term2', 'term3'].
-    m = Pattern.compile("%(\\d+)").matcher(rule);
-    Map<Integer, String[]> wildCardMap = new HashMap<>();
-    while (m.find()) {
-      int key = Integer.parseInt(m.group(1));
-      if (!wildCardMap.containsKey(key)) {
-        String wildcard = getWildcardContents(m.group(), row);
-        String[] terms = wildcard != null ? wildcard.split("\\|") : new String[] {null};
-        wildCardMap.put(key, terms);
-      }
-    }
-
-    // If both wildcard map & column name map are empty then the rule contained no wildcards. Just
-    // return it as it is:
-    if (wildCardMap.isEmpty() && columnNameMap.isEmpty()) {
-      interpolatedRules.add(rule);
-      return interpolatedRules;
-    }
-
-    // Interpolate based on column names and/or wildcards
-    interpolatedRules = interpolateColumnNames(columnNameMap, interpolatedRules, rule);
-    interpolatedRules = interpolateWildcards(wildCardMap, interpolatedRules, rule);
-    return interpolatedRules;
-  }
-
-  /**
-   * Given the string `format` and a number of formatting variables, use the formatting variables to
-   * fill in the format string in the manner of C's printf function, and write the string to the
-   * Writer object (or XLSX workbook, or Jinja context) that belongs to ValidateOperation. If the
-   * parameter `showCoords` is true, then include the current row and column number in the output
-   * string.
-   *
-   * @param format output format (txt, xlsx, etc.)
-   * @param positionalArgs table, row, column
-   */
-  private void report(String format, Object... positionalArgs) {
-    // Any report of error means validation failed
-    valid = false;
-
-    // Format the error message
-    String outStr = String.format("At %s row %d, column %d: ", currentTable, rowNum, colNum + 1);
-    outStr += String.format(format, positionalArgs);
-    if (!silent) {
-      // Print error if not silent
-      System.out.println(outStr);
-    }
-
-    if (outFormat != null && !outFormat.equals("txt")) {
-      // We want to put formatting on cells with errors
-      if (outFormat.equals("xlsx")) {
-        // Set the style of the current cell to a red background with a white font:
-        currentCell.setFontColor(IndexedColors.WHITE);
-        currentCell.setCellPattern(FillPatternType.FINE_DOTS);
-        currentCell.setCellColor(IndexedColors.RED);
-      } else {
-        // Set the HTML class to bg-danger (red background with a white font)
-        currentCell.setHTMLClass("bg-danger");
-      }
-      // Attach a comment to the cell
-      // If one for this cell already exists, add new comment to existing comment
-      String commentString = String.format(format, positionalArgs);
-      String currentComment = currentCell.getComment();
-      if (currentComment != null) {
-        commentString = currentComment + "; " + commentString;
-      }
-      currentCell.setComment(commentString);
-    } else if (outFormat != null) {
-      // Add outStr to messages to be written to file
-      messages.add(outStr);
-    }
-  }
-
-  /**
-   * Given a string describing a rule type, return a boolean indicating whether it is one of the
-   * rules recognized by ValidateOperation.
-   *
-   * @param ruleType string rule type
-   * @return true if the rule type is valid
-   */
-  private boolean unknownRuleType(String ruleType) {
-    return !rule_type_to_rtenum_map.containsKey(ruleType.split("\\|")[0]);
-  }
-
-  /**
-   * Given a string describing the content of a rule and a string describing its rule type, return a
-   * simple map entry such that the `key` for the entry is the main clause of the rule, and the
-   * `value` for the entry is a list of the rule's when-clauses. Each when-clause is itself stored
-   * as an array of three strings, including the subject to which the when-clause is to be applied,
-   * the rule type for the when clause, and the actual axiom to be validated against the subject.
-   *
-   * @param rule String rule
-   * @param ruleType String rule type
-   * @return map of main clause of rule to the values
-   */
-  private AbstractMap.SimpleEntry<String, List<String[]>> separateRule(String rule, String ruleType)
-      throws Exception {
-
-    // Check if there are any when clauses:
-    Matcher m = Pattern.compile("(\\(\\s*when\\s+.+\\))(.*)").matcher(rule);
-    String whenClauseStr;
-    if (!m.find()) {
-      // If there is no when clause, then just return back the rule string as it was passed with an
-      // empty when clause list:
-      logger.debug(String.format("No when-clauses found in rule: \"%s\".", rule));
-      return new AbstractMap.SimpleEntry<>(rule, new ArrayList<>());
-    }
-
-    // Throw an exception if there is no main clause and this is not a PRESENCE rule:
-    if (m.start() == 0 && rule_type_to_rtenum_map.get(ruleType).getRuleCat() != RCatEnum.PRESENCE) {
-      throw new Exception(String.format(noMainError, colNum + 1, rule));
-    }
-
-    // Extract the actual content of the when-clause.
-    whenClauseStr = m.group(1);
-    whenClauseStr = whenClauseStr.substring("(when ".length(), whenClauseStr.length() - 1);
-
-    // Don't fail just because there is some extra garbage at the end of the rule, but notify
-    // the user about it:
-    if (!m.group(2).trim().equals("")) {
-      logger.warn(
-          String.format("Ignoring string \"%s\" at end of rule \"%s\".", m.group(2).trim(), rule));
-    }
-
-    // Within each when clause, multiple subclauses separated by ampersands are allowed. Each
-    // subclass must be of the form: <Entity> <Rule-Type> <Axiom>, where: <Entity> is a (not
-    // necessarily interpolated) string describing either a label or a generalised DL class
-    // expression involving labels, and any label names containing spaces are enclosed within
-    // single quotes; <Rule-Type> is a possibly hyphenated alphanumeric string (which corresponds
-    // to one of the rule types defined above in RTypeEnum); and <Axiom> can take any form.
-    // Here we resolve each sub-clause of the when statement into a list of such triples.
-    ArrayList<String[]> whenClauses = new ArrayList<>();
-    for (String whenClause : whenClauseStr.split("\\s*&\\s*")) {
-      m =
-          Pattern.compile("^([^\'\\s()]+|\'[^\']+\'|\\(.+?\\))" + "\\s+([a-z\\-|]+)" + "\\s+(.*)$")
-              .matcher(whenClause);
-
-      if (!m.find()) {
-        throw new Exception(String.format(malformedWhenClauseError, colNum + 1, whenClause));
-      }
-      // Add the triple to the list of when clauses:
-      whenClauses.add(new String[] {m.group(1), m.group(2), m.group(3)});
-    }
-
-    // Now get the main part of the rule (i.e. the part before the when clause):
-    m = Pattern.compile("^(.+)\\s+\\(when\\s").matcher(rule);
-    if (m.find()) {
-      return new AbstractMap.SimpleEntry<>(m.group(1), whenClauses);
-    }
-
-    // If no main clause is found, then if this is a PRESENCE rule, implicitly assume that the main
-    // clause is "true":
-    if (rule_type_to_rtenum_map.get(ruleType).getRuleCat() == RCatEnum.PRESENCE) {
-      return new AbstractMap.SimpleEntry<>("true", whenClauses);
-    }
-
-    // We should never get here since we have already checked for an empty main clause earlier ...
-    logger.error(
-        String.format(
-            "Encountered unknown error while looking for main clause of rule \"%s\".", rule));
-    // Return the rule as passed with an empty when clause list:
-    return new AbstractMap.SimpleEntry<>(rule, new ArrayList<>());
-  }
-
-  /**
-   * Given a string describing a rule, a rule of the type PRESENCE, and a string representing a cell
-   * from the CSV, determine whether the cell satisfies the given presence rule (e.g. is-required,
-   * is-empty).
-   *
-   * @param rule String rule
-   * @param rType RTypeEnum (PRESENCE)
-   * @param cell cell contents
-   * @return validation message on issue, null on success
-   */
-  private String validatePresenceRule(String rule, RTypeEnum rType, String cell) throws Exception {
-
-    logger.debug(
-        String.format(
-            "validate_presence_rule(): Called with parameters: "
-                + "rule: \"%s\", "
-                + "rule type: \"%s\", "
-                + "cell: \"%s\".",
-            rule, rType.getRuleType(), cell));
-
-    // Presence-type rules (is-required, is-excluded) must be in the form of a truth value:
-    if ((Arrays.asList("true", "t", "1", "yes", "y").indexOf(rule.toLowerCase()) == -1)
-        && (Arrays.asList("false", "f", "0", "no", "n").indexOf(rule.toLowerCase()) == -1)) {
-      throw new Exception(
-          String.format(invalidPresenceRuleError, colNum + 1, rule, rType.getRuleType()));
-    }
-
-    // If the restriction isn't "true" then there is nothing to do. Just return:
-    if (Arrays.asList("true", "t", "1", "yes", "y").indexOf(rule.toLowerCase()) == -1) {
-      logger.debug(
-          String.format("Nothing to validate for rule: \"%s %s\"", rType.getRuleType(), rule));
-      return null;
-    }
-
-    String msg;
-    switch (rType) {
-      case REQUIRED:
-        if (cell.trim().equals("")) {
-          msg =
-              String.format(
-                  "Cell is empty but rule: \"%s %s\" does not allow this.",
-                  rType.getRuleType(), rule);
-          report(msg);
-          return msg;
-        }
-        break;
-      case EXCLUDED:
-        if (!cell.trim().equals("")) {
-          msg =
-              String.format(
-                  "Cell is non-empty (\"%s\") but rule: \"%s %s\" does not allow this.",
-                  cell, rType.getRuleType(), rule);
-          report(msg);
-          return msg;
-        }
-        break;
-      default:
-        msg =
-            String.format(
-                "%s validation of rule type: \"%s\" is not yet implemented.",
-                rType.getRuleCat(), rType.getRuleType());
-        logger.error(msg);
-        return msg;
-    }
-    logger.info(
-        String.format("Validated \"%s %s\" against \"%s\".", rType.getRuleType(), rule, cell));
-    return null;
-  }
-
-  /**
-   * Given a string describing a cell from the CSV, a string describing a rule to be applied against
-   * that cell, a string describing the type of that rule, and a list of strings describing the row
-   * containing the given cell, validate the cell, indicating any validation errors via the output
-   * writer (or XLSX workbook).
-   *
-   * @param cell cell contents of cell being validated
-   * @param rule rule to validate cell
-   * @param row current row being validated
-   * @param ruleType String rule type
-   * @return validation message on issue, null on success
-   */
-  private String validateRule(String cell, String rule, List<String> row, String ruleType)
-      throws Exception {
-    logger.debug(
-        String.format(
-            "validate_rule(): Called with parameters: "
-                + "cell: \"%s\", "
-                + "rule: \"%s\", "
-                + "row: \"%s\", "
-                + "rule type: \"%s\".",
-            cell, rule, row, ruleType));
-
-    logger.info(String.format("Validating rule \"%s %s\" against \"%s\".", ruleType, rule, cell));
-    if (unknownRuleType(ruleType)) {
-      throw new Exception(String.format(unrecognizedRuleTypeError, colNum + 1, ruleType));
-    }
-
-    // Separate the given rule into its main clause and optional when clauses:
-    AbstractMap.SimpleEntry<String, List<String[]>> separatedRule = separateRule(rule, ruleType);
-
-    // Evaluate and validate any when clauses for this rule first:
-    if (!validateWhenClauses(separatedRule.getValue(), row, colNum)) {
-      logger.debug("Not all when clauses have been satisfied. Skipping main clause");
-      return null;
-    }
-
-    // Once all of the when clauses have been validated, get the RTypeEnum representation of the
-    // primary rule type of this rule:
-    RTypeEnum primRType = rule_type_to_rtenum_map.get(ruleType.split("\\|")[0]);
-
-    // If the primary rule type for this rule is not in the QUERY category, process it at this step
-    // and return control to the caller. The further steps below are only needed when queries are
-    // going to be sent to the reasoner.
-    if (primRType.getRuleCat() != RCatEnum.QUERY) {
-      return validatePresenceRule(separatedRule.getKey(), primRType, cell);
-    }
-
-    // If the cell contents are empty, just return to the caller silently (if the cell is not
-    // expected to be empty, this will have been caught by one of the presence rules in the
-    // previous step, assuming such a rule is constraining the column).
-    if (cell.trim().equals("")) return null;
-
-    // Get the axiom that the cell will be validated against:
-    String axiom = separatedRule.getKey();
-
-    // Send the query to the reasoner:
-    // Comment may be null on exception, empty on success, or a non-empty String on validation
-    // failure
-    // Non-empty strings get added to the Cell
-    boolean result = executeQuery(cell, axiom, row, ruleType);
-    String msg = null;
-    if (!result) {
-      msg = String.format("Validation failed for rule: \"%s %s %s\".", cell, ruleType, axiom);
-      report(msg);
-    } else {
-      logger.info(String.format("Validated: \"%s %s %s\".", cell, ruleType, axiom));
-    }
-    return msg;
-  }
-
-  /**
-   * Given a list of String arrays describing a list of when-clauses, and a list of Strings
-   * describing the row to which these when-clauses belong, validate the when-clauses one by one,
-   * returning false if any of them fails to be satisfied, and true if they are all satisfied.
-   *
-   * @param whenClauses clauses to validate
-   * @param row row to validate
-   * @param colNum number of current column to validate
-   * @return true if all conditions are satisfied
-   */
-  private boolean validateWhenClauses(List<String[]> whenClauses, List<String> row, int colNum)
-      throws Exception {
-
-    for (String[] whenClause : whenClauses) {
-      String subject = whenClause[0].trim();
-      // If the subject term is blank, then skip this clause:
-      if (subject.equals("")) {
-        continue;
-      }
-
-      // Make sure all of the rule types in the when clause are of the right category:
-      String whenRuleType = whenClause[1];
-      for (String whenRuleSubType : whenRuleType.split("\\|")) {
-        RTypeEnum whenSubRType = rule_type_to_rtenum_map.get(whenRuleSubType);
-        if (whenSubRType == null || whenSubRType.getRuleCat() != RCatEnum.QUERY) {
-          throw new Exception(
-              String.format(
-                  invalidWhenTypeError,
-                  colNum + 1,
-                  String.join(" ", whenClause),
-                  query_type_to_rtenum_map.keySet()));
-        }
-      }
-
-      // Get the axiom to validate and send the query to the reasoner:
-      String axiom = whenClause[2];
-      if (!executeQuery(subject, axiom, row, whenRuleType)) {
-        // If any of the when clauses fail to be satisfied, then we do not need to evaluate any
-        // of the other when clauses, or the main clause, since the main clause may only be
-        // evaluated when all of the when clauses are satisfied.
-        logger.info(
-            String.format(
-                "When clause: \"%s %s %s\" is not satisfied.", subject, whenRuleType, axiom));
-        return false;
-      } else {
-        logger.info(
-            String.format("Validated when clause \"%s %s %s\".", subject, whenRuleType, axiom));
-      }
-    }
-    // If we get to here, then all of the when clauses have been satisfied, so return true:
-    return true;
-  }
-
-  /**
-   * An enum representation of the different categories of rules. We distinguish between queries,
-   * which involve queries to a reasoner, and presence rules, which check for the existence of
-   * content in a cell.
-   */
-  private enum RCatEnum {
-    QUERY,
-    PRESENCE
-  }
-
-  /**
-   * An enum representation of the different types of rules. Each rule type belongs to larger
-   * category, and is identified within the CSV file by a particular string.
-   */
-  private enum RTypeEnum {
-    DIRECT_SUPER("direct-superclass-of", RCatEnum.QUERY),
-    NOT_SUPER("not-superclass-of", RCatEnum.QUERY),
-    NOT_DIRECT_SUPER("not-direct-superclass-of", RCatEnum.QUERY),
-    SUPER("superclass-of", RCatEnum.QUERY),
-    EQUIV("equivalent-to", RCatEnum.QUERY),
-    NOT_EQUIV("not-equivalent-to", RCatEnum.QUERY),
-    DIRECT_SUB("direct-subclass-of", RCatEnum.QUERY),
-    NOT_SUB("not-subclass-of", RCatEnum.QUERY),
-    NOT_DIRECT_SUB("not-direct-subclass-of", RCatEnum.QUERY),
-    SUB("subclass-of", RCatEnum.QUERY),
-    DIRECT_INSTANCE("direct-instance-of", RCatEnum.QUERY),
-    NOT_INSTANCE("not-instance-of", RCatEnum.QUERY),
-    INSTANCE("instance-of", RCatEnum.QUERY),
-    REQUIRED("is-required", RCatEnum.PRESENCE),
-    EXCLUDED("is-excluded", RCatEnum.PRESENCE);
-
-    private final String ruleType;
-    private final RCatEnum ruleCat;
-
-    RTypeEnum(String ruleType, RCatEnum ruleCat) {
-      this.ruleType = ruleType;
-      this.ruleCat = ruleCat;
-    }
-
-    private String getRuleType() {
-      return ruleType;
-    }
-
-    private RCatEnum getRuleCat() {
-      return ruleCat;
-    }
-  }
-}
diff --git a/robot-core/src/main/java/org/obolibrary/robot/Template.java b/robot-core/src/main/java/org/obolibrary/robot/Template.java
index 3a473c6d..e87a50c3 100644
--- a/robot-core/src/main/java/org/obolibrary/robot/Template.java
+++ b/robot-core/src/main/java/org/obolibrary/robot/Template.java
@@ -870,34 +870,40 @@ public class Template {
         // Subclass expression
         subclassExpressionColumns.put(
             column,
-            TemplateHelper.getClassExpressions(name, parser, template, value, rowNum, column));
+            TemplateHelper.getClassExpressions(
+                name, checker, parser, template, value, rowNum, column));
       } else if (template.startsWith("EC")) {
         // Equivalent expression
         equivalentExpressionColumns.put(
             column,
-            TemplateHelper.getClassExpressions(name, parser, template, value, rowNum, column));
+            TemplateHelper.getClassExpressions(
+                name, checker, parser, template, value, rowNum, column));
       } else if (template.startsWith("DC")) {
         // Disjoint expression
         disjointExpressionColumns.put(
             column,
-            TemplateHelper.getClassExpressions(name, parser, template, value, rowNum, column));
+            TemplateHelper.getClassExpressions(
+                name, checker, parser, template, value, rowNum, column));
       } else if (template.startsWith("C") && !template.startsWith("CLASS_TYPE")) {
         // Use class type to determine what to do with the expression
         switch (classType) {
           case "subclass":
             subclassExpressionColumns.put(
                 column,
-                TemplateHelper.getClassExpressions(name, parser, template, value, rowNum, column));
+                TemplateHelper.getClassExpressions(
+                    name, checker, parser, template, value, rowNum, column));
             break;
           case "equivalent":
             intersectionEquivalentExpressionColumns.put(
                 column,
-                TemplateHelper.getClassExpressions(name, parser, template, value, rowNum, column));
+                TemplateHelper.getClassExpressions(
+                    name, checker, parser, template, value, rowNum, column));
             break;
           case "disjoint":
             disjointExpressionColumns.put(
                 column,
-                TemplateHelper.getClassExpressions(name, parser, template, value, rowNum, column));
+                TemplateHelper.getClassExpressions(
+                    name, checker, parser, template, value, rowNum, column));
             break;
           default:
             break;
@@ -1163,12 +1169,14 @@ public class Template {
       } else if (template.startsWith("DOMAIN")) {
         // Handle domains
         Set<OWLClassExpression> expressions =
-            TemplateHelper.getClassExpressions(name, parser, template, value, rowNum, column);
+            TemplateHelper.getClassExpressions(
+                name, checker, parser, template, value, rowNum, column);
         addObjectPropertyDomains(property, expressions, row, column);
       } else if (template.startsWith("RANGE")) {
         // Handle ranges
         Set<OWLClassExpression> expressions =
-            TemplateHelper.getClassExpressions(name, parser, template, value, rowNum, column);
+            TemplateHelper.getClassExpressions(
+                name, checker, parser, template, value, rowNum, column);
         addObjectPropertyRanges(property, expressions, row, column);
       }
     }
@@ -1439,7 +1447,8 @@ public class Template {
       } else if (template.startsWith("DOMAIN")) {
         // Handle domains
         Set<OWLClassExpression> expressions =
-            TemplateHelper.getClassExpressions(name, parser, template, value, rowNum, column);
+            TemplateHelper.getClassExpressions(
+                name, checker, parser, template, value, rowNum, column);
         addDataPropertyDomains(property, expressions, row, column);
       } else if (template.startsWith("RANGE")) {
         // Handle ranges
@@ -1845,7 +1854,7 @@ public class Template {
         } else {
           // If the class is null, assume it is a class expression
           OWLClassExpression typeExpr =
-              TemplateHelper.tryParse(name, parser, type, rowNum, typeColumn);
+              TemplateHelper.tryParse(name, checker, parser, type, rowNum, typeColumn);
           axioms.add(dataFactory.getOWLClassAssertionAxiom(typeExpr, individual));
         }
       }
@@ -1896,7 +1905,8 @@ public class Template {
           template = template + " SPLIT=" + split;
         }
         Set<OWLClassExpression> typeExpressions =
-            TemplateHelper.getClassExpressions(name, parser, template, value, rowNum, column);
+            TemplateHelper.getClassExpressions(
+                name, checker, parser, template, value, rowNum, column);
         for (OWLClassExpression ce : typeExpressions) {
           axioms.add(dataFactory.getOWLClassAssertionAxiom(ce, individual));
         }
diff --git a/robot-core/src/main/java/org/obolibrary/robot/TemplateHelper.java b/robot-core/src/main/java/org/obolibrary/robot/TemplateHelper.java
index 23dd0e79..75330188 100644
--- a/robot-core/src/main/java/org/obolibrary/robot/TemplateHelper.java
+++ b/robot-core/src/main/java/org/obolibrary/robot/TemplateHelper.java
@@ -243,6 +243,34 @@ public class TemplateHelper {
       int rowNum,
       int col)
       throws RowParseException {
+    return getClassExpressions(tableName, null, parser, template, value, rowNum, col);
+  }
+
+  /**
+   * Given a Quoted Entity checker, a Manchester Syntax parser, a template string, and a template
+   * value, insert the value into the template string and parse to a set of class expressions. If
+   * the template string contains 'SPLIT=', the value will be split on the provided character.
+   * Otherwise, the set will only have one entry.
+   *
+   * @param tableName name of table
+   * @param checker QuotedEntityChecker to resolve labels
+   * @param parser ManchesterOWLSyntaxClassExpressionParser to parse expression
+   * @param template template string
+   * @param value value to replace '%' in template string
+   * @param rowNum the line number
+   * @param col the column number
+   * @return set of OWLClassExpressions
+   * @throws RowParseException if row is malformed
+   */
+  public static Set<OWLClassExpression> getClassExpressions(
+      String tableName,
+      QuotedEntityChecker checker,
+      ManchesterOWLSyntaxClassExpressionParser parser,
+      String template,
+      String value,
+      int rowNum,
+      int col)
+      throws RowParseException {
     String split = getSplit(template);
     template = getTemplate(template);
 
@@ -253,20 +281,21 @@ public class TemplateHelper {
         String[] values = value.split(Pattern.quote(split));
         for (String v : values) {
           String content = QuotedEntityChecker.wrap(v);
-          expressions.add(tryParse(tableName, parser, content, rowNum, col));
+          expressions.add(tryParse(tableName, checker, parser, content, rowNum, col));
         }
       } else {
         String content = QuotedEntityChecker.wrap(value);
-        expressions.add(tryParse(tableName, parser, content, rowNum, col));
+        expressions.add(tryParse(tableName, checker, parser, content, rowNum, col));
       }
     } else {
       if (split != null) {
         String[] values = value.split(Pattern.quote(split));
         for (String v : values) {
-          expressions.add(getClassExpression(tableName, template, v, parser, rowNum, col));
+          expressions.add(getClassExpression(tableName, template, v, checker, parser, rowNum, col));
         }
       } else {
-        expressions.add(getClassExpression(tableName, template, value, parser, rowNum, col));
+        expressions.add(
+            getClassExpression(tableName, template, value, checker, parser, rowNum, col));
       }
     }
     return expressions;
@@ -927,11 +956,46 @@ public class TemplateHelper {
       int rowNum,
       int column)
       throws RowParseException {
+    return tryParse(tableName, null, parser, content, rowNum, column);
+  }
+
+  /**
+   * Given a Quoted Entity Checker to resolve labels, a Manchester class expression parser, and a
+   * content string, try to parse the content string. If the checker is not null, first try to get a
+   * named class. If not found, try to parse using the parser. Throw a detailed exception message if
+   * parsing fails.
+   *
+   * @param tableName name of table
+   * @param checker QuotedEntityChecker to resolve labels
+   * @param parser ManchesterOWLSyntaxClassExpressionParser to parse string
+   * @param content class expression string to parse
+   * @param rowNum the row number for logging
+   * @param column the column number for logging
+   * @return OWLClassExpression representation of the string
+   * @throws RowParseException if string cannot be parsed for any reason
+   */
+  protected static OWLClassExpression tryParse(
+      String tableName,
+      QuotedEntityChecker checker,
+      ManchesterOWLSyntaxClassExpressionParser parser,
+      String content,
+      int rowNum,
+      int column)
+      throws RowParseException {
     OWLClassExpression expr;
     content = content.trim();
     logger.info(String.format("Parsing expression: %s", content));
     try {
-      expr = parser.parse(content);
+      expr = null;
+      if (checker != null) {
+        // If we have a checker, try to get the class by label
+        // This allows class labels with single quotes
+        expr = checker.getOWLClass(content);
+      }
+      if (expr == null) {
+        // If not found by label, try to parse
+        expr = parser.parse(content);
+      }
     } catch (OWLParserException e) {
       String cause = getManchesterErrorCause(e);
 
@@ -1052,6 +1116,7 @@ public class TemplateHelper {
       String tableName,
       String template,
       String value,
+      QuotedEntityChecker checker,
       ManchesterOWLSyntaxClassExpressionParser parser,
       int rowNum,
       int col)
@@ -1064,7 +1129,7 @@ public class TemplateHelper {
     } else {
       sub = content;
     }
-    return tryParse(tableName, parser, sub, rowNum, col);
+    return tryParse(tableName, checker, parser, sub, rowNum, col);
   }
 
   /**
diff --git a/robot-core/src/main/java/org/obolibrary/robot/ValidateOperation.java b/robot-core/src/main/java/org/obolibrary/robot/ValidateOperation.java
deleted file mode 100644
index bff413f1..00000000
--- a/robot-core/src/main/java/org/obolibrary/robot/ValidateOperation.java
+++ /dev/null
@@ -1,164 +0,0 @@
-package org.obolibrary.robot;
-
-import java.util.*;
-import java.util.stream.Collectors;
-import org.semanticweb.owlapi.apibinding.OWLManager;
-import org.semanticweb.owlapi.manchestersyntax.parser.ManchesterOWLSyntaxClassExpressionParser;
-import org.semanticweb.owlapi.model.*;
-import org.semanticweb.owlapi.reasoner.OWLReasoner;
-import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;
-import org.semanticweb.owlapi.util.SimpleShortFormProvider;
-
-/**
- * Implements the validate operation on a collection of tables
- *
- * @author <a href="mailto:consulting@michaelcuffaro.com">Michael E. Cuffaro</a>
- */
-public class ValidateOperation {
-
-  /** Namespace for error messages. */
-  private static final String NS = "validate#";
-
-  private static final String missingHeadersError =
-      NS
-          + "MISSING HEADERS ERROR --rules table headers must include at least: table, column, validation";
-
-  private static final String multipleRulesError =
-      NS + "MULTIPLE RULES ERROR column '%s' has more than one rule defined in --rules";
-
-  /**
-   * Return the default Validate options.
-   *
-   * @return map of default options
-   */
-  public static Map<String, String> getDefaultOptions() {
-    Map<String, String> options = new HashMap<>();
-    options.put("format", null);
-    options.put("standalone", "true");
-    options.put("output-dir", null);
-    options.put("silent", "true");
-    options.put("errors", null);
-    options.put("skip-row", "0");
-    options.put("write-all", "false");
-    return options;
-  }
-
-  /**
-   * Validate tables based on an ontology.
-   *
-   * @param rules map of table name to column to rule
-   * @param tables tables to validate (map of table name to table contents)
-   * @param ontology OWLOntology to use to validate tables
-   * @param ioHelper IOHelper to resolve entities
-   * @param reasonerFactory OWLReasonerFactory to create reasoner
-   * @param options map of validate options
-   * @throws Exception on any problem
-   */
-  public static List<String> validate(
-      Map<String, Map<String, String>> rules,
-      Map<String, List<List<String>>> tables,
-      OWLOntology ontology,
-      IOHelper ioHelper,
-      OWLReasonerFactory reasonerFactory,
-      Map<String, String> options)
-      throws Exception {
-    if (options == null) {
-      options = getDefaultOptions();
-    }
-
-    // Robot's custom quoted entity checker will be used for parsing class expressions:
-    QuotedEntityChecker checker = new QuotedEntityChecker();
-    // Add the class that will be used for I/O and for handling short-form IRIs by the quoted entity
-    // checker:
-    checker.setIOHelper(new IOHelper());
-    checker.addProvider(new SimpleShortFormProvider());
-
-    // Initialise the dataFactory and use it to add rdfs:label to the list of annotation properties
-    // which will be looked up in the ontology by the quoted entity checker when finding names.
-    OWLDataFactory dataFactory = OWLManager.getOWLDataFactory();
-    checker.addProperty(dataFactory.getRDFSLabel());
-    checker.addAll(ontology);
-
-    // Create the parser using the data factory and entity checker.
-    ManchesterOWLSyntaxClassExpressionParser parser =
-        new ManchesterOWLSyntaxClassExpressionParser(dataFactory, checker);
-
-    // Use the given reasonerFactory to initialise the reasoner based on the given ontology:
-    OWLReasoner reasoner = reasonerFactory.createReasoner(ontology);
-    String outFormat = options.getOrDefault("format", null);
-    String outDir = options.getOrDefault("output-dir", ".");
-
-    TableValidator validator =
-        new TableValidator(ontology, ioHelper, parser, reasoner, outFormat, outDir);
-
-    boolean silent = OptionsHelper.optionIsTrue(options, "silent");
-    if (silent && outFormat != null) {
-      // Only toggle to silent if results are written to a file
-      validator.toggleLogging();
-    }
-
-    // Run validation over all tables
-    List<String> result = validator.validate(rules, tables, options);
-
-    // Maybe save errors to their own table
-    String errorsPath = OptionsHelper.getOption(options, "errors", null);
-    if (errorsPath != null) {
-      List<String[]> errors = validator.getErrors();
-      if (errors.size() > 1) {
-        // Only one item in the errors means it is just the header
-        IOHelper.writeTable(errors, errorsPath);
-      }
-    }
-    return result;
-  }
-
-  /**
-   * Get the set of validation rules from a table.
-   *
-   * @param rulesPath path to rules table
-   * @return map of table name to column to rule
-   * @throws Exception on any IO or formatting issue
-   */
-  public static Map<String, Map<String, String>> getRules(String rulesPath) throws Exception {
-    Map<String, Map<String, String>> rules = new HashMap<>();
-    List<List<String>> rulesData;
-    if (rulesPath.endsWith(".csv")) {
-      rulesData = IOHelper.readCSV(rulesPath);
-    } else {
-      rulesData = IOHelper.readCSV(rulesPath);
-    }
-
-    List<String> rulesHeader = rulesData.remove(0);
-    rulesHeader = rulesHeader.stream().map(String::toLowerCase).collect(Collectors.toList());
-    if (!rulesHeader.containsAll(Arrays.asList("validation", "table", "column"))) {
-      throw new Exception(missingHeadersError);
-    }
-
-    int ruleIdx = rulesHeader.indexOf("validation");
-    int tableIdx = rulesHeader.indexOf("table");
-    int colIdx = rulesHeader.indexOf("column");
-    for (List<String> row : rulesData) {
-      String table = row.get(tableIdx);
-      String column = row.get(colIdx);
-
-      // Maybe get a rule
-      if (row.size() <= ruleIdx) {
-        continue;
-      }
-      String rule = row.get(ruleIdx);
-      if (rule.trim().equals("")) {
-        continue;
-      }
-
-      // Add rule to rules
-      Map<String, String> columnRules = rules.getOrDefault(table, new HashMap<>());
-      if (columnRules.containsKey(column)) {
-        throw new Exception(String.format(multipleRulesError, column));
-      }
-      columnRules.put(column, row.get(ruleIdx));
-      rules.put(table, columnRules);
-    }
-
-    return rules;
-  }
-}


Test patch:
diff --git a/robot-core/src/test/java/org/obolibrary/robot/TemplateTest.java b/robot-core/src/test/java/org/obolibrary/robot/TemplateTest.java
index c6a1925e..68d05e51 100644
--- a/robot-core/src/test/java/org/obolibrary/robot/TemplateTest.java
+++ b/robot-core/src/test/java/org/obolibrary/robot/TemplateTest.java
@@ -5,8 +5,9 @@ import static org.junit.Assert.assertEquals;
 import com.google.common.collect.Lists;
 import java.util.*;
 import org.junit.Test;
-import org.semanticweb.owlapi.model.IRI;
-import org.semanticweb.owlapi.model.OWLOntology;
+import org.semanticweb.owlapi.apibinding.OWLManager;
+import org.semanticweb.owlapi.manchestersyntax.parser.ManchesterOWLSyntaxClassExpressionParser;
+import org.semanticweb.owlapi.model.*;
 
 /**
  * Tests Template class and class methods.
@@ -130,4 +131,29 @@ public class TemplateTest extends CoreTest {
         template, IRI.create("https://github.com/ontodev/robot/examples/template.owl"), null);
     assertIdentical("/docs-template.owl", template);
   }
+
+  /**
+   * Test parsing a label that contains single quotes.
+   *
+   * @throws Exception on problem parsing
+   */
+  @Test
+  public void testParseClass() throws Exception {
+    OWLDataFactory df = OWLManager.getOWLDataFactory();
+    String label = "Streptococcus sp. 'group B'";
+    OWLClass expectedClass =
+        df.getOWLClass(IRI.create("http://purl.obolibrary.org/obo/NCBITaxon_1319"));
+
+    // Create the checker
+    QuotedEntityChecker checker = new QuotedEntityChecker();
+    checker.add(expectedClass, label);
+
+    // Create the parser
+    ManchesterOWLSyntaxClassExpressionParser parser =
+        new ManchesterOWLSyntaxClassExpressionParser(df, checker);
+
+    OWLClass actualClass =
+        TemplateHelper.tryParse("test", checker, parser, label, 0, 0).asOWLClass();
+    assert actualClass.getIRI().toString().equals(expectedClass.getIRI().toString());
+  }
 }
diff --git a/robot-core/src/test/java/org/obolibrary/robot/ValidateOperationTest.java b/robot-core/src/test/java/org/obolibrary/robot/ValidateOperationTest.java
deleted file mode 100644
index db2d6af5..00000000
--- a/robot-core/src/test/java/org/obolibrary/robot/ValidateOperationTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package org.obolibrary.robot;
-
-import static org.junit.Assert.*;
-
-import java.io.*;
-import java.net.URL;
-import java.nio.file.Paths;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.apache.commons.io.FileUtils;
-import org.junit.Test;
-import org.semanticweb.HermiT.ReasonerFactory;
-import org.semanticweb.owlapi.model.OWLOntology;
-import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;
-
-/** Tests for ValidateOperationTest */
-public class ValidateOperationTest extends CoreTest {
-
-  /**
-   * Test of the validation operation on a sample 'immune exposures' CSV file.
-   *
-   * @throws IOException on any problem
-   */
-  @Test
-  public void testImmuneExposuresValidation() throws Exception {
-    IOHelper ioHelper = new IOHelper();
-
-    InputStream tableStream = this.getClass().getResourceAsStream("/immune_exposures.csv");
-    assert (tableStream != null);
-    List<List<String>> tableData = IOHelper.readCSV(tableStream);
-    assert (tableData != null);
-
-    URL res = this.getClass().getResource("/immune_exposures_rules.csv");
-    File file = Paths.get(res.toURI()).toFile();
-    String rulesPath = file.getAbsolutePath();
-
-    Map<String, Map<String, String>> rules = ValidateOperation.getRules(rulesPath);
-
-    res = this.getClass().getResource("/immune_exposures.csv");
-    file = Paths.get(res.toURI()).toFile();
-    String tablePath = file.getAbsolutePath();
-
-    Map<String, List<List<String>>> tables = new HashMap<>();
-    tables.put(tablePath, tableData);
-
-    InputStream owlStream = this.getClass().getResourceAsStream("/immune_exposures.owl");
-    assert (owlStream != null);
-    OWLOntology ontology = ioHelper.loadOntology(owlStream);
-    assert (ontology != null);
-
-    // Redirect STDOUT to an OutputStream wrapped in a PrintStream:
-    ByteArrayOutputStream outStream = new ByteArrayOutputStream();
-    PrintStream prStream = new PrintStream(outStream);
-    System.setOut(prStream);
-
-    // Call validate() with an outputPath of null to send output to STDOUT:
-    OWLReasonerFactory reasonerFactory = new ReasonerFactory();
-    ValidateOperation.validate(rules, tables, ontology, ioHelper, reasonerFactory, null);
-
-    // Compare the output with the contents of a file in the resources directory which contains
-    // the output we expect to get:
-    String fileWithExpectedContents =
-        this.getClass().getResource("/immune_exposures-result.txt").getPath();
-    assertNotEquals(fileWithExpectedContents, "");
-    String expectedResult = FileUtils.readFileToString(new File(fileWithExpectedContents));
-
-    assertEquals(outStream.toString(), expectedResult);
-  }
-}
diff --git a/robot-core/src/test/resources/immune_exposures-result.txt b/robot-core/src/test/resources/immune_exposures-result.txt
deleted file mode 100644
index 677e6072..00000000
--- a/robot-core/src/test/resources/immune_exposures-result.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-Validating immune_exposures.csv ...
-At immune_exposures.csv row 18, column 2: Cell is empty but rule: "is-required true" does not allow this.
-At immune_exposures.csv row 20, column 4: Cell is empty but rule: "is-required true" does not allow this.
-At immune_exposures.csv row 21, column 6: Cell is empty but rule: "is-required true" does not allow this.
-At immune_exposures.csv row 22, column 2: Validation failed for rule: "Dengue virus equivalent-to 'Dengue virus 2'".
-At immune_exposures.csv row 22, column 3: Validation failed for rule: "NCBITaxon_11060 equivalent-to 'Dengue virus'".
-At immune_exposures.csv row 23, column 4: Validation failed for rule: "Dengue virus 2 subclass-of 'disease name'".
-At immune_exposures.csv row 23, column 4: Validation failed for rule: "Dengue virus 2 subclass-of hasMaterialBasisIn some 'Dengue virus'".
-At immune_exposures.csv row 24, column 2: Validation failed for rule: "'Hepatovirus A' or blood equivalent-to 'blood'".
-At immune_exposures.csv row 24, column 3: Validation failed for rule: "UBERON_0000178 equivalent-to ('Hepatovirus A' or blood)".
-At immune_exposures.csv row 24, column 6: Cell is non-empty ("chronic") but rule: "is-excluded true" does not allow this.
-At immune_exposures.csv row 25, column 4: Validation failed for rule: "wheat allergy equivalent-to 'dengue hemorrhagic fever'".
-At immune_exposures.csv row 25, column 4: Validation failed for rule: "dengue hemorrhagic fever equivalent-to 'wheat allergy'".
-At immune_exposures.csv row 25, column 4: Validation failed for rule: "wheat allergy subclass-of hasMaterialBasisIn some 'Dengue virus'".
-At immune_exposures.csv row 25, column 5: Validation failed for rule: "DOID_3660 equivalent-to 'dengue hemorrhagic fever'".
-At immune_exposures.csv row 25, column 5: Validation failed for rule: "DOID_12206 equivalent-to 'wheat allergy'".
diff --git a/robot-core/src/test/resources/immune_exposures.csv b/robot-core/src/test/resources/immune_exposures.csv
deleted file mode 100644
index 81b024a7..00000000
--- a/robot-core/src/test/resources/immune_exposures.csv
+++ /dev/null
@@ -1,26 +0,0 @@
-exposure process reported,exposure material reported,exposure material id,disease reported,disease ontology id,disease stage reported,comment
-vaccination,Varicella-zoster virus vaccine,VO_0000669,,,,PASS: vacciniation has only material
-infectious challenge,Hepacivirus C,NCBITaxon_11103,,,,PASS: infectious challenge requires only material
-transplant/transfusion,blood,UBERON_0000178,,,,PASS: transplant requires only material
-infectious disease,Dengue virus,NCBITaxon_12637,dengue hemorrhagic fever,DOID_12206,acute/recent onset,"PASS: infectious disease requires material, disease, stage"
-allergic disease,Triticum aestivum ,NCBITaxon_4565,wheat allergy,DOID_3660,post,"PASS: allergic disease requires material, disease, stage"
-autoimmune disease,,,acquired immunodeficiency syndrome,DOID_635,chronic,"PASS: autoimmune disease requires disease, state; excludes material"
-cancer,,,colon cancer,DOID_219,chronic,"PASS: cancer requires disease, state; excludes material"
-asymptomatic infection/colonization,Dengue virus,NCBITaxon_12637,,,,PASS: asymptomatic requires only material
-exposure with immune reactivity,Dengue virus 2,NCBITaxon_11060,,,,PASS: exposure with immune reactivity requires only material
-exposure with documentation,Dengue virus,NCBITaxon_12637,,,,PASS: exposure with documentation requires only material
-exposure to endemic/ubiquitous agent,Dengue virus,NCBITaxon_12637,,,,PASS: exposure to endemic requires only material
-no exposure,,,,,,PASS: none requires no fields
-unknown,,,,,,PASS: unknown requires no fields
-administration,Hepacivirus C,NCBITaxon_11103,,,,PASS
-exposure without disease,Dengue virus,NCBITaxon_12637,,,,PASS
-disease,,,,,,PASS
-infectious disease,,,dengue hemorrhagic fever,DOID_12206,acute/recent onset,FAIL: material required
-disease or 'infectious challenge',,,'wheat allergy' or 'dengue hemorrhagic fever',DOID_12206 or DOID_3660,'acute/recent onset' or chronic,PASS
-infectious disease,Dengue virus,NCBITaxon_12637,,,acute/recent onset,FAIL: disease required
-infectious disease,Dengue virus,NCBITaxon_12637,dengue hemorrhagic fever,DOID_12206,,FAIL: stage required
-infectious disease,Dengue virus,NCBITaxon_11060,dengue hemorrhagic fever,DOID_12206,acute/recent onset,FAIL: exposure material must match exposure material ID
-infectious disease,Dengue virus,NCBITaxon_12637,Dengue virus 2,NCBITaxon_11060,acute/recent onset,FAIL: disease value must be a disease
-administration or 'exposure without disease','Hepatovirus A' or blood,UBERON_0000178,,,chronic,"FAIL: exposure material must match exposure material ID, disease must be excluded when exposure process is not a subclass of disease"
-infectious disease,Dengue virus,NCBITaxon_12637,dengue hemorrhagic fever|wheat allergy,DOID_12206|DOID_3660,acute/recent onset,FAIL: not all combinations of the entries in the multi-cells for columns D and E satisfy the given rules
-infectious disease,Dengue virus,NCBITaxon_12637,dengue hemorrhagic fever|dengue hemorrhagic fever,DOID_12206|DOID_12206,acute/recent onset,PASS
diff --git a/robot-core/src/test/resources/immune_exposures.owl b/robot-core/src/test/resources/immune_exposures.owl
deleted file mode 100644
index c2df4f71..00000000
--- a/robot-core/src/test/resources/immune_exposures.owl
+++ /dev/null
@@ -1,667 +0,0 @@
-<?xml version="1.0"?>
-<rdf:RDF xmlns="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#"
-     xml:base="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology"
-     xmlns:owl="http://www.w3.org/2002/07/owl#"
-     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:xml="http://www.w3.org/XML/1998/namespace"
-     xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
-     xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
-    <owl:Ontology rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology"/>
-    
-
-
-    <!-- 
-    ///////////////////////////////////////////////////////////////////////////////////////
-    //
-    // Object Properties
-    //
-    ///////////////////////////////////////////////////////////////////////////////////////
-     -->
-
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasDisease -->
-
-    <owl:ObjectProperty rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasDisease">
-        <owl:inverseOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#isDiseaseOf"/>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AsymmetricProperty"/>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#IrreflexiveProperty"/>
-    </owl:ObjectProperty>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasDiseaseStage -->
-
-    <owl:ObjectProperty rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasDiseaseStage">
-        <owl:inverseOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#isDiseaseStageOf"/>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AsymmetricProperty"/>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#IrreflexiveProperty"/>
-    </owl:ObjectProperty>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasExposureMaterial -->
-
-    <owl:ObjectProperty rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasExposureMaterial">
-        <owl:inverseOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#isExposureMaterialOf"/>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AsymmetricProperty"/>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#IrreflexiveProperty"/>
-    </owl:ObjectProperty>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasMaterialBasisIn -->
-
-    <owl:ObjectProperty rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasMaterialBasisIn"/>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#isDiseaseOf -->
-
-    <owl:ObjectProperty rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#isDiseaseOf">
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AsymmetricProperty"/>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#IrreflexiveProperty"/>
-    </owl:ObjectProperty>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#isDiseaseStageOf -->
-
-    <owl:ObjectProperty rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#isDiseaseStageOf">
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AsymmetricProperty"/>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#IrreflexiveProperty"/>
-    </owl:ObjectProperty>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#isExposureMaterialOf -->
-
-    <owl:ObjectProperty rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#isExposureMaterialOf">
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AsymmetricProperty"/>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#IrreflexiveProperty"/>
-    </owl:ObjectProperty>
-    
-
-
-    <!-- 
-    ///////////////////////////////////////////////////////////////////////////////////////
-    //
-    // Classes
-    //
-    ///////////////////////////////////////////////////////////////////////////////////////
-     -->
-
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/CORE_0000100 -->
-
-    <owl:Class rdf:about="http://purl.obolibrary.org/obo/CORE_0000100">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureMaterial"/>
-        <rdfs:label xml:lang="en">organism</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/DOID_12206 -->
-
-    <owl:Class rdf:about="http://purl.obolibrary.org/obo/DOID_12206">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#http://purl.obolibrary.org/obo/DOID_4"/>
-        <rdfs:subClassOf>
-            <owl:Restriction>
-                <owl:onProperty rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasMaterialBasisIn"/>
-                <owl:someValuesFrom rdf:resource="http://purl.obolibrary.org/obo/NCBITaxon_12637"/>
-            </owl:Restriction>
-        </rdfs:subClassOf>
-        <rdfs:label>dengue hemorrhagic fever</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/DOID_219 -->
-
-    <owl:Class rdf:about="http://purl.obolibrary.org/obo/DOID_219">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#http://purl.obolibrary.org/obo/DOID_4"/>
-        <rdfs:label>colon cancer</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/DOID_3660 -->
-
-    <owl:Class rdf:about="http://purl.obolibrary.org/obo/DOID_3660">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#http://purl.obolibrary.org/obo/DOID_4"/>
-        <rdfs:subClassOf>
-            <owl:Restriction>
-                <owl:onProperty rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasMaterialBasisIn"/>
-                <owl:someValuesFrom rdf:resource="http://purl.obolibrary.org/obo/NCBITaxon_4565"/>
-            </owl:Restriction>
-        </rdfs:subClassOf>
-        <rdfs:label>wheat allergy</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/DOID_635 -->
-
-    <owl:Class rdf:about="http://purl.obolibrary.org/obo/DOID_635">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#http://purl.obolibrary.org/obo/DOID_4"/>
-        <rdfs:label>acquired immunodeficiency syndrome</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/NCBITaxon_11060 -->
-
-    <owl:Class rdf:about="http://purl.obolibrary.org/obo/NCBITaxon_11060">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/NCBITaxon_12637"/>
-        <rdfs:label>Dengue virus 2</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/NCBITaxon_12637 -->
-
-    <owl:Class rdf:about="http://purl.obolibrary.org/obo/NCBITaxon_12637">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/CORE_0000100"/>
-        <rdfs:label>Dengue virus</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/NCBITaxon_4565 -->
-
-    <owl:Class rdf:about="http://purl.obolibrary.org/obo/NCBITaxon_4565">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/CORE_0000100"/>
-        <rdfs:label>Triticum aestivum</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/UBERON_0000178 -->
-
-    <owl:Class rdf:about="http://purl.obolibrary.org/obo/UBERON_0000178">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/UBERON_0001062"/>
-        <rdfs:label>blood</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/UBERON_0001062 -->
-
-    <owl:Class rdf:about="http://purl.obolibrary.org/obo/UBERON_0001062">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureMaterial"/>
-        <rdfs:label>anatomical entity</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0000001 -->
-
-    <owl:Class rdf:about="http://purl.obolibrary.org/obo/VO_0000001">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureMaterial"/>
-        <rdfs:label>vaccine</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://purl.obolibrary.org/obo/VO_0000669 -->
-
-    <owl:Class rdf:about="http://purl.obolibrary.org/obo/VO_0000669">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/VO_0000001"/>
-        <rdfs:label>Varicella-zoster virus vaccine</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AcuteOrRecentOnset -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AcuteOrRecentOnset">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DiseaseStage"/>
-        <rdfs:label>acute/recent onset</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AdministrationExposure -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AdministrationExposure">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureProcess"/>
-        <rdfs:subClassOf>
-            <owl:Restriction>
-                <owl:onProperty rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasExposureMaterial"/>
-                <owl:someValuesFrom rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureMaterial"/>
-            </owl:Restriction>
-        </rdfs:subClassOf>
-        <rdfs:label>administration</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AllergicDisease -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AllergicDisease">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DiseaseExposure"/>
-        <rdfs:subClassOf>
-            <owl:Restriction>
-                <owl:onProperty rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasExposureMaterial"/>
-                <owl:someValuesFrom rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureMaterial"/>
-            </owl:Restriction>
-        </rdfs:subClassOf>
-        <rdfs:label>allergic disease</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AsymptomaticInfectionOrColonization -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AsymptomaticInfectionOrColonization">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureWithoutDisease"/>
-        <rdfs:label>asymptomatic infection/colonization</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AutoImmuneDisease -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AutoImmuneDisease">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DiseaseExposure"/>
-        <rdfs:label>autoimmune disease</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#Cancer -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#Cancer">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DiseaseExposure"/>
-        <rdfs:label>cancer</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#Chronic -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#Chronic">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DiseaseStage"/>
-        <rdfs:label>chronic</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DOID_12549 -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DOID_12549">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#http://purl.obolibrary.org/obo/DOID_4"/>
-        <rdfs:subClassOf>
-            <owl:Restriction>
-                <owl:onProperty rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasMaterialBasisIn"/>
-                <owl:someValuesFrom rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#NCBITaxon_12092"/>
-            </owl:Restriction>
-        </rdfs:subClassOf>
-        <rdfs:label>hepatitis A</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DOID_1883 -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DOID_1883">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#http://purl.obolibrary.org/obo/DOID_4"/>
-        <rdfs:subClassOf>
-            <owl:Restriction>
-                <owl:onProperty rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasMaterialBasisIn"/>
-                <owl:someValuesFrom rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#NCBITaxon_11103"/>
-            </owl:Restriction>
-        </rdfs:subClassOf>
-        <rdfs:label>hepatitis C</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DOID_2043 -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DOID_2043">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#http://purl.obolibrary.org/obo/DOID_4"/>
-        <rdfs:subClassOf>
-            <owl:Restriction>
-                <owl:onProperty rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasMaterialBasisIn"/>
-                <owl:someValuesFrom rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#NCBITaxon_10407"/>
-            </owl:Restriction>
-        </rdfs:subClassOf>
-        <rdfs:label>hepatitis B</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DiseaseExposure -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DiseaseExposure">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureProcess"/>
-        <rdfs:subClassOf>
-            <owl:Restriction>
-                <owl:onProperty rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasDisease"/>
-                <owl:someValuesFrom rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#http://purl.obolibrary.org/obo/DOID_4"/>
-            </owl:Restriction>
-        </rdfs:subClassOf>
-        <rdfs:subClassOf>
-            <owl:Restriction>
-                <owl:onProperty rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasDiseaseStage"/>
-                <owl:someValuesFrom rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DiseaseStage"/>
-            </owl:Restriction>
-        </rdfs:subClassOf>
-        <rdfs:label>disease</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DiseaseStage -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DiseaseStage">
-        <rdfs:label>disease stage</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureMaterial -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureMaterial">
-        <rdfs:label>exposure material</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureProcess -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureProcess">
-        <rdfs:label>exposure process</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureToEndemicOrUbiquitousAgent -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureToEndemicOrUbiquitousAgent">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureWithoutDisease"/>
-        <rdfs:label>exposure to endemic/ubiquitous agent</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureWithDocumentation -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureWithDocumentation">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureWithoutDisease"/>
-        <rdfs:label>exposure with documentation</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureWithImmuneReactivity -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureWithImmuneReactivity">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureWithoutDisease"/>
-        <rdfs:label>exposure with immune reactivity</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureWithoutDisease -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureWithoutDisease">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureProcess"/>
-        <rdfs:subClassOf>
-            <owl:Restriction>
-                <owl:onProperty rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasExposureMaterial"/>
-                <owl:someValuesFrom rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureMaterial"/>
-            </owl:Restriction>
-        </rdfs:subClassOf>
-        <rdfs:label>exposure without disease</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#InfectiousChallenge -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#InfectiousChallenge">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AdministrationExposure"/>
-        <rdfs:label>infectious challenge</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#InfectiousDisease -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#InfectiousDisease">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DiseaseExposure"/>
-        <rdfs:subClassOf>
-            <owl:Restriction>
-                <owl:onProperty rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#hasExposureMaterial"/>
-                <owl:someValuesFrom rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureMaterial"/>
-            </owl:Restriction>
-        </rdfs:subClassOf>
-        <rdfs:label>infectious disease</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#NCBITaxon_10407 -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#NCBITaxon_10407">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/CORE_0000100"/>
-        <rdfs:label>Hepatitis B virus</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#NCBITaxon_11103 -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#NCBITaxon_11103">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/CORE_0000100"/>
-        <rdfs:label>Hepacivirus C</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#NCBITaxon_12092 -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#NCBITaxon_12092">
-        <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/CORE_0000100"/>
-        <rdfs:label>Hepatovirus A</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#NoExposure -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#NoExposure">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureProcess"/>
-        <rdfs:label>no exposure</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#Post -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#Post">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DiseaseStage"/>
-        <rdfs:label>post</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#TransplantOrTransfusion -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#TransplantOrTransfusion">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AdministrationExposure"/>
-        <rdfs:label>transplant/transfusion</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#UnknownExposure -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#UnknownExposure">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureProcess"/>
-        <rdfs:label>unknown</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#Vaccination -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#Vaccination">
-        <rdfs:subClassOf rdf:resource="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AdministrationExposure"/>
-        <rdfs:label>vaccination</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#http://purl.obolibrary.org/obo/DOID_4 -->
-
-    <owl:Class rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#http://purl.obolibrary.org/obo/DOID_4">
-        <rdfs:label>disease name</rdfs:label>
-    </owl:Class>
-    
-
-
-    <!-- 
-    ///////////////////////////////////////////////////////////////////////////////////////
-    //
-    // Individuals
-    //
-    ///////////////////////////////////////////////////////////////////////////////////////
-     -->
-
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DOCID1 -->
-
-    <owl:NamedIndividual rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DOCID1">
-        <rdfs:label>Harry Smith</rdfs:label>
-    </owl:NamedIndividual>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DOCID2 -->
-
-    <owl:NamedIndividual rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DOCID2">
-        <rdfs:label>Jolin Wu</rdfs:label>
-    </owl:NamedIndividual>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DOCID3 -->
-
-    <owl:NamedIndividual rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DOCID3">
-        <rdfs:label>Pele</rdfs:label>
-    </owl:NamedIndividual>
-    
-
-
-    <!-- http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DOCID4 -->
-
-    <owl:NamedIndividual rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DOCID4">
-        <rdfs:label>Travis Semenko</rdfs:label>
-    </owl:NamedIndividual>
-    
-
-
-    <!-- 
-    ///////////////////////////////////////////////////////////////////////////////////////
-    //
-    // General axioms
-    //
-    ///////////////////////////////////////////////////////////////////////////////////////
-     -->
-
-    <rdf:Description>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AllDisjointClasses"/>
-        <owl:members rdf:parseType="Collection">
-            <rdf:Description rdf:about="http://purl.obolibrary.org/obo/CORE_0000100"/>
-            <rdf:Description rdf:about="http://purl.obolibrary.org/obo/UBERON_0001062"/>
-            <rdf:Description rdf:about="http://purl.obolibrary.org/obo/VO_0000001"/>
-        </owl:members>
-    </rdf:Description>
-    <rdf:Description>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AllDisjointClasses"/>
-        <owl:members rdf:parseType="Collection">
-            <rdf:Description rdf:about="http://purl.obolibrary.org/obo/DOID_12206"/>
-            <rdf:Description rdf:about="http://purl.obolibrary.org/obo/DOID_219"/>
-            <rdf:Description rdf:about="http://purl.obolibrary.org/obo/DOID_3660"/>
-            <rdf:Description rdf:about="http://purl.obolibrary.org/obo/DOID_635"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DOID_12549"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DOID_1883"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DOID_2043"/>
-        </owl:members>
-    </rdf:Description>
-    <rdf:Description>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AllDisjointClasses"/>
-        <owl:members rdf:parseType="Collection">
-            <rdf:Description rdf:about="http://purl.obolibrary.org/obo/NCBITaxon_12637"/>
-            <rdf:Description rdf:about="http://purl.obolibrary.org/obo/NCBITaxon_4565"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#NCBITaxon_10407"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#NCBITaxon_11103"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#NCBITaxon_12092"/>
-        </owl:members>
-    </rdf:Description>
-    <rdf:Description>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AllDisjointClasses"/>
-        <owl:members rdf:parseType="Collection">
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AcuteOrRecentOnset"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#Chronic"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#Post"/>
-        </owl:members>
-    </rdf:Description>
-    <rdf:Description>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AllDisjointClasses"/>
-        <owl:members rdf:parseType="Collection">
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AdministrationExposure"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DiseaseExposure"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureWithoutDisease"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#NoExposure"/>
-        </owl:members>
-    </rdf:Description>
-    <rdf:Description>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AllDisjointClasses"/>
-        <owl:members rdf:parseType="Collection">
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AllergicDisease"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AutoImmuneDisease"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#Cancer"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#InfectiousDisease"/>
-        </owl:members>
-    </rdf:Description>
-    <rdf:Description>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AllDisjointClasses"/>
-        <owl:members rdf:parseType="Collection">
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#AsymptomaticInfectionOrColonization"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureToEndemicOrUbiquitousAgent"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureWithDocumentation"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureWithImmuneReactivity"/>
-        </owl:members>
-    </rdf:Description>
-    <rdf:Description>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AllDisjointClasses"/>
-        <owl:members rdf:parseType="Collection">
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#DiseaseStage"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureMaterial"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#ExposureProcess"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#http://purl.obolibrary.org/obo/DOID_4"/>
-        </owl:members>
-    </rdf:Description>
-    <rdf:Description>
-        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AllDisjointClasses"/>
-        <owl:members rdf:parseType="Collection">
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#InfectiousChallenge"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#TransplantOrTransfusion"/>
-            <rdf:Description rdf:about="http://www.semanticweb.org/mike/ontologies/2019/6/immune-exposures-ontology#Vaccination"/>
-        </owl:members>
-    </rdf:Description>
-</rdf:RDF>
-
-
-
-<!-- Generated by the OWL API (version 4.5.9.2019-02-01T07:24:44Z) https://github.com/owlcs/owlapi -->
-
diff --git a/robot-core/src/test/resources/immune_exposures_rules.csv b/robot-core/src/test/resources/immune_exposures_rules.csv
deleted file mode 100644
index 14382380..00000000
--- a/robot-core/src/test/resources/immune_exposures_rules.csv
+++ /dev/null
@@ -1,7 +0,0 @@
-table,column,validation
-immune_exposures,exposure process reported,is-required; subclass-of 'exposure process';
-immune_exposures,exposure material reported,subclass-of|equivalent-to 'exposure material'; equivalent-to %3; is-required (when %1 subclass-of (administration or 'exposure without disease' or 'infectious disease' or 'allergic disease')); is-excluded (when %1 equivalent-to disease); is-excluded (when %1 subclass-of ('autoimmune disease' or cancer or 'no exposure' or unknown))
-immune_exposures,exposure material id,equivalent-to %2
-immune_exposures,disease reported,subclass-of 'disease name'; subclass-of hasMaterialBasisIn some %2 (when %1 subclass-of not ('autoimmune disease' or cancer)); equivalent-to %5; is-required (when %1 subclass-of disease); is-excluded (when %1 equivalent-to administration); is-excluded (when %1 subclass-of (vaccination or transplant/transfusion or 'exposure without disease' or 'no exposure' or unknown))
-immune_exposures,disease ontology id,equivalent-to %4
-immune_exposures,disease stage reported,subclass-of 'disease stage'; is-required (when %1 subclass-of disease); is-excluded (when %1 subclass-of not disease)