Repository: dice-group/owlapy (language: python)

Issue:
Refactoring completed



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 00000000..95a2e886
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,57 @@
+name: Build & Publish Docs
+
+on:
+  push:
+    branches:
+     - main
+     - develop
+     - documentation # just for testing
+  pull_request:
+
+jobs:
+  docs:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: [ "3.10.11" ]
+      max-parallel: 5
+
+    steps:
+    - uses: actions/checkout@v3
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v4
+      with:
+        python-version: ${{ matrix.python-version }}
+
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        pip install -e . 
+        pip install sphinx sphinx_rtd_theme sphinx-autoapi sphinx-theme sphinxcontrib-plantuml plantuml-local-client myst-parser
+
+    - name: Prepare required software
+      run: |
+        # epstopdf & dot & noto-fonts
+        sudo apt update && sudo apt install texlive-font-utils graphviz fonts-noto\
+
+    - name: Build docs
+      run: |
+        sphinx-build -M html docs/ docs/_build/
+
+    - name: Build LaTeX docs
+      run: |
+        sphinx-build -M latex docs/ docs/_build/
+
+    - name: Compile LaTeX document
+      uses: docker://texlive/texlive:latest
+      with:
+        args: make -C docs/_build/latex
+    - name: Copy Latex pdf to ./html
+      run: |
+        cp docs/_build/latex/owlapy.pdf docs/_build/html/
+
+    - name: Deploy to GitHub Pages
+      uses: peaceiris/actions-gh-pages@v3
+      with:
+        github_token: ${{ secrets.GITHUB_TOKEN }}
+        publish_dir: 'docs/_build/html'
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..4224f44d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,148 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.json
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+docs/_static_gen/
+# managed by sphinx autosummary in api.rst
+docs/source/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+#   For a library or package, you might want to ignore these files since the code is
+#   intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+#   However, in case of collaboration, if having platform-specific dependencies or dependencies
+#   having no cross-platform support, pipenv may install dependencies that don't work, or not
+#   install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# Pycharm IDE
+.idea/
+
+# VS Code
+.vscode/
\ No newline at end of file
diff --git a/README.md b/README.md
index 4512ebd2..d35e99b2 100644
--- a/README.md
+++ b/README.md
@@ -21,29 +21,26 @@ In this example we start with a simple atomic class expression and move to some
 ones and finally render and print the last of them in description logics syntax.
 
 ```python
-from owlapy.render import DLSyntaxObjectRenderer
-from owlapy.model import IRI, OWLClass, OWLObjectProperty, OWLObjectSomeValuesFrom, \
-                         OWLObjectIntersectionOf
+from owlapy.iri import IRI
+from owlapy.class_expression import OWLClass, OWLObjectIntersectionOf, OWLObjectSomeValuesFrom
+from owlapy.owl_property import OWLObjectProperty
 from owlapy.owl2sparql.converter import owl_expression_to_sparql
-
-# Create an IRI object using the iri as a string for 'male' class.
-male_iri = IRI.create('http://example.com/society#male')
-
+from owlapy.render import owl_expression_to_dl
 # Create the male class
-male = OWLClass(male_iri)
+male = OWLClass("http://example.com/society#male")
 
 # Create an object property using the iri as a string for 'hasChild' property.
-hasChild = OWLObjectProperty(IRI.create('http://example.com/society#hasChild'))
+hasChild = OWLObjectProperty("http://example.com/society#hasChild")
 
 # Create an existential restrictions
 males_with_children = OWLObjectSomeValuesFrom(hasChild, male)
 
 # Let's make it more complex by intersecting with another class
-teacher = OWLClass(IRI.create('http://example.com/society#teacher'))
+teacher = OWLClass("http://example.com/society#teacher")
 male_teachers_with_children = OWLObjectIntersectionOf([males_with_children, teacher])
 
-# You can render and print owl class expressions in description logics syntax
-print(DLSyntaxObjectRenderer().render(male_teachers_with_children)) 
+# You can render and print owl class expressions in description logics syntax (and vice-versa)
+print(owl_expression_to_dl(male_teachers_with_children))
 # (∃ hasChild.male) ⊓ teacher
 print(owl_expression_to_sparql("?x", male_teachers_with_children))
 #  SELECT DISTINCT ?x WHERE {  ?x <http://example.com/society#hasChild> ?s_1 . ?s_1 a <http://example.com/society#male> . ?x a <http://example.com/society#teacher> .  } }
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 00000000..d4bb2cbb
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
+SOURCEDIR     = .
+BUILDDIR      = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/_static/documentation_options_patch.js b/docs/_static/documentation_options_patch.js
new file mode 100644
index 00000000..c43a72bb
--- /dev/null
+++ b/docs/_static/documentation_options_patch.js
@@ -0,0 +1,1 @@
+DOCUMENTATION_OPTIONS.LINK_SUFFIX = DOCUMENTATION_OPTIONS.FILE_SUFFIX
diff --git a/docs/_static/js/theme.js b/docs/_static/js/theme.js
new file mode 100644
index 00000000..2f013342
--- /dev/null
+++ b/docs/_static/js/theme.js
@@ -0,0 +1,243 @@
+var jQuery = (typeof(window) != 'undefined') ? window.jQuery : require('jquery');
+
+// Sphinx theme nav state
+function ThemeNav () {
+
+    var nav = {
+        navBar: null,
+        win: null,
+        winScroll: false,
+        winResize: false,
+        linkScroll: false,
+        winPosition: 0,
+        winHeight: null,
+        docHeight: null,
+        isRunning: false
+    };
+
+    nav.enable = function (withStickyNav) {
+        var self = this;
+
+        // TODO this can likely be removed once the theme javascript is broken
+        // out from the RTD assets. This just ensures old projects that are
+        // calling `enable()` get the sticky menu on by default. All other cals
+        // to `enable` should include an argument for enabling the sticky menu.
+        if (typeof(withStickyNav) == 'undefined') {
+            withStickyNav = true;
+        }
+
+        if (self.isRunning) {
+            // Only allow enabling nav logic once
+            return;
+        }
+
+        self.isRunning = true;
+        jQuery(function ($) {
+            self.init($);
+
+            self.reset();
+            self.win.on('hashchange', self.reset);
+
+            if (withStickyNav) {
+                // Set scroll monitor
+                self.win.on('scroll', function () {
+                    if (!self.linkScroll) {
+                        if (!self.winScroll) {
+                            self.winScroll = true;
+                            requestAnimationFrame(function() { self.onScroll(); });
+                        }
+                    }
+                });
+            }
+
+            // Set resize monitor
+            self.win.on('resize', function () {
+                if (!self.winResize) {
+                    self.winResize = true;
+                    requestAnimationFrame(function() { self.onResize(); });
+                }
+            });
+
+            self.onResize();
+        });
+
+    };
+
+    // TODO remove this with a split in theme and Read the Docs JS logic as
+    // well, it's only here to support 0.3.0 installs of our theme.
+    nav.enableSticky = function() {
+        this.enable(true);
+    };
+
+    nav.init = function ($) {
+        var doc = $(document),
+            self = this;
+
+        this.navBar = $('div.wy-side-scroll:first');
+        this.win = $(window);
+
+        // Set up javascript UX bits
+        $(document)
+            // Shift nav in mobile when clicking the menu.
+            .on('click', "[data-toggle='wy-nav-top']", function() {
+                $("[data-toggle='wy-nav-shift']").toggleClass("shift");
+                $("[data-toggle='rst-versions']").toggleClass("shift");
+            })
+
+            // Nav menu link click operations
+            .on('click', ".wy-menu-vertical .current ul li a", function() {
+                var target = $(this);
+                // Close menu when you click a link.
+                $("[data-toggle='wy-nav-shift']").removeClass("shift");
+                $("[data-toggle='rst-versions']").toggleClass("shift");
+                // Handle dynamic display of l3 and l4 nav lists
+                self.toggleCurrent(target);
+                self.hashChange();
+            })
+            .on('click', "[data-toggle='rst-current-version']", function() {
+                $("[data-toggle='rst-versions']").toggleClass("shift-up");
+            })
+
+        // Make tables responsive
+        $("table.docutils:not(.field-list,.footnote,.citation)")
+            .wrap("<div class='wy-table-responsive'></div>");
+
+        // Add extra class to responsive tables that contain
+        // footnotes or citations so that we can target them for styling
+        $("table.docutils.footnote")
+            .wrap("<div class='wy-table-responsive footnote'></div>");
+        $("table.docutils.citation")
+            .wrap("<div class='wy-table-responsive citation'></div>");
+
+        // Add expand links to all parents of nested ul
+        $('.wy-menu-vertical ul').not('.simple').siblings('a').each(function () {
+            var link = $(this);
+                expand = $('<span class="toctree-expand"></span>');
+            expand.on('click', function (ev) {
+                self.toggleCurrent(link);
+                ev.stopPropagation();
+                return false;
+            });
+            link.prepend(expand);
+        });
+    };
+
+    nav.reset = function () {
+        // Get anchor from URL and open up nested nav
+        var anchor = encodeURI(window.location.hash) || '#';
+
+        try {
+            var vmenu = $('.wy-menu-vertical');
+            var link = vmenu.find('[href="' + anchor + '"]');
+            if (link.length === 0) {
+                // this link was not found in the sidebar.
+                // Find associated id element, then its closest section
+                // in the document and try with that one.
+                var id_elt = $('.document [id="' + anchor.substring(1) + '"]');
+                var closest_section = id_elt.closest('div.section');
+                link = vmenu.find('[href="#' + closest_section.attr("id") + '"]');
+                if (link.length === 0) {
+                    // still not found in the sidebar. fall back to main section
+                    link = vmenu.find('[href="#"]');
+                }
+            }
+            // If we found a matching link then reset current and re-apply
+            // otherwise retain the existing match
+            if (link.length > 0) {
+                $('.wy-menu-vertical .current').removeClass('current');
+                link.addClass('current');
+                link.closest('li.toctree-l1').parent().addClass('current');
+                for (let i = 1; i <= 10; i++) {
+                    link.closest('li.toctree-l' + i).addClass('current');
+                }
+                link[0].scrollIntoView();
+            }
+        }
+        catch (err) {
+            console.log("Error expanding nav for anchor", err);
+        }
+
+    };
+
+    nav.onScroll = function () {
+        this.winScroll = false;
+        var newWinPosition = this.win.scrollTop(),
+            winBottom = newWinPosition + this.winHeight,
+            navPosition = this.navBar.scrollTop(),
+            newNavPosition = navPosition + (newWinPosition - this.winPosition);
+        if (newWinPosition < 0 || winBottom > this.docHeight) {
+            return;
+        }
+        this.navBar.scrollTop(newNavPosition);
+        this.winPosition = newWinPosition;
+    };
+
+    nav.onResize = function () {
+        this.winResize = false;
+        this.winHeight = this.win.height();
+        this.docHeight = $(document).height();
+    };
+
+    nav.hashChange = function () {
+        this.linkScroll = true;
+        this.win.one('hashchange', function () {
+            this.linkScroll = false;
+        });
+    };
+
+    nav.toggleCurrent = function (elem) {
+        var parent_li = elem.closest('li');
+        parent_li.siblings('li.current').removeClass('current');
+        parent_li.siblings().find('li.current').removeClass('current');
+        var children = parent_li.find('> ul li');
+        // Don't toggle terminal elements.
+        if (children.length) {
+            children.removeClass('current');
+            parent_li.toggleClass('current');
+        }
+    }
+
+    return nav;
+};
+
+module.exports.ThemeNav = ThemeNav();
+
+if (typeof(window) != 'undefined') {
+    window.SphinxRtdTheme = {
+        Navigation: module.exports.ThemeNav,
+        // TODO remove this once static assets are split up between the theme
+        // and Read the Docs. For now, this patches 0.3.0 to be backwards
+        // compatible with a pre-0.3.0 layout.html
+        StickyNav: module.exports.ThemeNav,
+    };
+}
+
+
+// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
+// https://gist.github.com/paulirish/1579671
+// MIT license
+
+(function() {
+    var lastTime = 0;
+    var vendors = ['ms', 'moz', 'webkit', 'o'];
+    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
+        window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
+        window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
+                                   || window[vendors[x]+'CancelRequestAnimationFrame'];
+    }
+
+    if (!window.requestAnimationFrame)
+        window.requestAnimationFrame = function(callback, element) {
+            var currTime = new Date().getTime();
+            var timeToCall = Math.max(0, 16 - (currTime - lastTime));
+            var id = window.setTimeout(function() { callback(currTime + timeToCall); },
+              timeToCall);
+            lastTime = currTime + timeToCall;
+            return id;
+        };
+
+    if (!window.cancelAnimationFrame)
+        window.cancelAnimationFrame = function(id) {
+            clearTimeout(id);
+        };
+}());
diff --git a/docs/_static/theme_tweak.css b/docs/_static/theme_tweak.css
new file mode 100644
index 00000000..aeedc7fa
--- /dev/null
+++ b/docs/_static/theme_tweak.css
@@ -0,0 +1,9 @@
+.rst-content dl:not(.docutils) dl dl dt {
+    width: auto;
+}
+.rst-content dl:not(.docutils) dl dt {
+    width: 96%;
+}
+.rst-content dl:not(.docutils) dt {
+    width: 100%;
+}
diff --git a/docs/_templates/search.html b/docs/_templates/search.html
new file mode 100644
index 00000000..07a4b787
--- /dev/null
+++ b/docs/_templates/search.html
@@ -0,0 +1,2 @@
+{% extends "!search.html" %}
+{% set script_files = [ '_static/documentation_options_patch.js', '_static/language_data.js' ] + script_files %}
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 00000000..bf08a50a
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,114 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Project information -----------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+
+import os
+import sys
+
+sys.path.insert(0, os.path.abspath(".."))
+
+project = 'OWLAPY'
+author = 'Ontolearn Team'
+release = '0.1.2'
+
+# -- General configuration ---------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+
+extensions = ["autoapi.extension",
+              "sphinx.ext.githubpages",
+              "sphinx.ext.todo",
+              "sphinx.ext.napoleon",
+              "sphinx.ext.viewcode",
+              "sphinx.ext.autodoc",
+              "sphinxcontrib.plantuml",
+              "myst_parser",
+              "sphinx_rtd_theme",
+              ]
+
+
+autoapi_dirs = ['../owlapy']
+
+# by default all are included but had to reinitialize this to remove private members from showing
+autoapi_options = ['members', 'undoc-members', 'show-inheritance', 'show-module-summary', 'special-members',
+                   'imported-members']
+
+# this is set to false, so we can add it manually in index.rst together with the other .md files of the documentation.
+autoapi_add_toctree_entry = False
+
+inheritance_graph_attrs = dict(rankdir="TB")
+
+myst_enable_extensions = [
+    'colon_fence',
+    'deflist',
+]
+
+myst_heading_anchors = 3
+
+templates_path = ['_templates']
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+# -- Options for HTML output -------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
+
+pygments_style = 'rainbow_dash'
+
+plantuml_output_format = 'svg_img'
+plantuml_latex_output_format = 'pdf'
+
+stanford_theme_mod = True
+html_theme_options = {
+    'navigation_depth': 6,
+}
+
+html_static_path = ['_static']
+
+if stanford_theme_mod:
+    html_theme = 'sphinx_rtd_theme'
+
+    def _import_theme():
+        import os
+        import shutil
+        import sphinx_theme
+        html_theme = 'stanford_theme'
+        for _type in ['fonts']:
+            shutil.copytree(
+                os.path.join(sphinx_theme.get_html_theme_path(html_theme),
+                             html_theme, 'static', _type),
+                os.path.join('_static_gen', _type),
+                dirs_exist_ok=True)
+        shutil.copy2(
+            os.path.join(sphinx_theme.get_html_theme_path(html_theme),
+                         html_theme, 'static', 'css', 'theme.css'),
+            os.path.join('_static_gen', 'theme.css'),
+        )
+
+    _import_theme()
+    html_static_path = ['_static_gen'] + html_static_path
+
+# -- Options for LaTeX output ------------------------------------------------
+
+latex_engine = 'xelatex'
+latex_show_urls = 'footnote'
+latex_theme = 'howto'
+
+latex_elements = {
+    'preamble': r'''
+\renewcommand{\pysiglinewithargsret}[3]{%
+  \item[{%
+      \parbox[t]{\linewidth}{\setlength{\hangindent}{12ex}%
+        \raggedright#1\sphinxcode{(}\linebreak[0]{\renewcommand{\emph}[1]{\mbox{\textit{##1}}}#2}\sphinxcode{)}\linebreak[0]\mbox{#3}}}]}
+''',
+    'printindex': '\\def\\twocolumn[#1]{#1}\\footnotesize\\raggedright\\printindex',
+}
+
+
+def setup(app):
+    # -- Options for HTML output ---------------------------------------------
+    if stanford_theme_mod:
+        app.add_css_file('theme.css')
+    app.add_css_file('theme_tweak.css')
+    app.add_css_file('pygments.css')
\ No newline at end of file
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 00000000..3e4858f3
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,14 @@
+
+
+Welcome to OWLAPY!
+===========================================
+
+`OWLAPY <https://github.com/dice-group/owlapy>`_: Representation of OWL objects in python.
+
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+
+   usage/main
+   autoapi/owlapy/index
\ No newline at end of file
diff --git a/docs/usage/main.md b/docs/usage/main.md
new file mode 100644
index 00000000..54b6f2f2
--- /dev/null
+++ b/docs/usage/main.md
@@ -0,0 +1,3 @@
+# OWLAPY
+
+placeholder
\ No newline at end of file
diff --git a/owlapy/__init__.py b/owlapy/__init__.py
index 10939f01..8ce9b362 100644
--- a/owlapy/__init__.py
+++ b/owlapy/__init__.py
@@ -1,1 +1,1 @@
-__version__ = '0.1.2'
+__version__ = '0.1.3'
diff --git a/owlapy/class_expression/__init__.py b/owlapy/class_expression/__init__.py
new file mode 100644
index 00000000..d6797156
--- /dev/null
+++ b/owlapy/class_expression/__init__.py
@@ -0,0 +1,40 @@
+"""https://www.w3.org/TR/owl2-syntax/#Class_Expressions
+ClassExpression :=
+    owl_class.py:
+    Class
+    nary_boolean_expression.py:
+    ObjectIntersectionOf, ObjectUnionOf
+    class_expression.py: ObjectComplementOf
+
+    restriction.py:
+    ObjectOneOf, ObjectSomeValuesFrom, ObjectAllValuesFrom, ObjectHasValue,ObjectHasSelf,
+    ObjectMinCardinality, ObjectMaxCardinality, ObjectExactCardinality, DataSomeValuesFrom, DataAllValuesFrom,
+    DataHasValue, DataMinCardinality, DataMaxCardinality, DataExactCardinality
+"""
+from .class_expression import OWLClassExpression, OWLAnonymousClassExpression, OWLBooleanClassExpression, \
+    OWLObjectComplementOf
+from .owl_class import OWLClass
+from .nary_boolean_expression import OWLNaryBooleanClassExpression, OWLObjectUnionOf, OWLObjectIntersectionOf
+from .restriction import (OWLRestriction, OWLQuantifiedRestriction, OWLQuantifiedObjectRestriction,
+                          OWLObjectRestriction,
+                          OWLHasValueRestriction, OWLDataRestriction, OWLCardinalityRestriction,
+                          OWLObjectCardinalityRestriction, OWLObjectHasSelf,
+                          OWLDataOneOf, OWLQuantifiedDataRestriction, OWLDataCardinalityRestriction,
+                          OWLObjectSomeValuesFrom, OWLObjectAllValuesFrom, OWLObjectHasValue,
+                          OWLDatatypeRestriction, OWLFacet, OWLFacetRestriction,
+                          OWLObjectMinCardinality,
+                          OWLObjectMaxCardinality,
+                          OWLObjectExactCardinality,
+                          OWLDataSomeValuesFrom,
+                          OWLDataAllValuesFrom,
+                          OWLDataHasValue,
+                          OWLDataMinCardinality,
+                          OWLDataMaxCardinality,
+                          OWLDataExactCardinality,
+                          OWLObjectOneOf
+                          )
+from typing import Final
+from ..vocab import OWLRDFVocabulary
+
+OWLThing: Final = OWLClass(OWLRDFVocabulary.OWL_THING.get_iri())  #: : :The OWL Class corresponding to owl:Thing
+OWLNothing: Final = OWLClass(OWLRDFVocabulary.OWL_NOTHING.get_iri())  #: : :The OWL Class corresponding to owl:Nothing
diff --git a/owlapy/class_expression/class_expression.py b/owlapy/class_expression/class_expression.py
new file mode 100644
index 00000000..50dbb4f0
--- /dev/null
+++ b/owlapy/class_expression/class_expression.py
@@ -0,0 +1,107 @@
+from abc import abstractmethod, ABCMeta
+from ..data_ranges import OWLPropertyRange, OWLDataRange
+from ..meta_classes import HasOperands
+
+from typing import Final, Iterable
+class OWLClassExpression(OWLPropertyRange):
+    """An OWL 2 Class Expression (https://www.w3.org/TR/owl2-syntax/#Class_Expressions) """
+    __slots__ = ()
+
+    @abstractmethod
+    def is_owl_thing(self) -> bool:
+        """Determines if this expression is the built in class owl:Thing. This method does not determine if the class
+        is equivalent to owl:Thing.
+
+        Returns:
+            True if this expression is owl:Thing.
+        """
+        pass
+
+    @abstractmethod
+    def is_owl_nothing(self) -> bool:
+        """Determines if this expression is the built in class owl:Nothing. This method does not determine if the class
+        is equivalent to owl:Nothing.
+        """
+        pass
+
+    @abstractmethod
+    def get_object_complement_of(self) -> 'OWLObjectComplementOf':
+        """Gets the object complement of this class expression.
+
+        Returns:
+            A class expression that is the complement of this class expression.
+        """
+        pass
+
+    @abstractmethod
+    def get_nnf(self) -> 'OWLClassExpression':
+        """Gets the negation normal form of the complement of this expression.
+
+        Returns:
+            A expression that represents the NNF of the complement of this expression.
+        """
+        pass
+
+
+class OWLAnonymousClassExpression(OWLClassExpression, metaclass=ABCMeta):
+    """A Class Expression which is not a named Class."""
+
+    def is_owl_nothing(self) -> bool:
+        # documented in parent
+        return False
+
+    def is_owl_thing(self) -> bool:
+        # documented in parent
+        return False
+
+    def get_object_complement_of(self) -> 'OWLObjectComplementOf':
+        # documented in parent
+        return OWLObjectComplementOf(self)
+
+    def get_nnf(self) -> 'OWLClassExpression':
+        # documented in parent
+        from owlapy.util import NNF
+        return NNF().get_class_nnf(self)
+
+
+class OWLBooleanClassExpression(OWLAnonymousClassExpression, metaclass=ABCMeta):
+    """Represent an anonymous boolean class expression."""
+    __slots__ = ()
+    pass
+
+
+class OWLObjectComplementOf(OWLBooleanClassExpression, HasOperands[OWLClassExpression]):
+    """Represents an ObjectComplementOf class expression in the OWL 2 Specification."""
+    __slots__ = '_operand'
+    type_index: Final = 3003
+
+    _operand: OWLClassExpression
+
+    def __init__(self, op: OWLClassExpression):
+        """
+        Args:
+            op: Class expression to complement.
+        """
+        self._operand = op
+
+    def get_operand(self) -> OWLClassExpression:
+        """
+        Returns:
+            The wrapped expression.
+        """
+        return self._operand
+
+    def operands(self) -> Iterable[OWLClassExpression]:
+        # documented in parent
+        yield self._operand
+
+    def __repr__(self):
+        return f"OWLObjectComplementOf({repr(self._operand)})"
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._operand == other._operand
+        return NotImplemented
+
+    def __hash__(self):
+        return hash(self._operand)
diff --git a/owlapy/class_expression/nary_boolean_expression.py b/owlapy/class_expression/nary_boolean_expression.py
new file mode 100644
index 00000000..682c8bc2
--- /dev/null
+++ b/owlapy/class_expression/nary_boolean_expression.py
@@ -0,0 +1,48 @@
+from .class_expression import OWLClassExpression, OWLBooleanClassExpression
+from ..meta_classes import HasOperands
+from typing import Final, Sequence, Iterable
+class OWLNaryBooleanClassExpression(OWLBooleanClassExpression, HasOperands[OWLClassExpression]):
+    """OWLNaryBooleanClassExpression."""
+    __slots__ = ()
+
+    _operands: Sequence[OWLClassExpression]
+
+    def __init__(self, operands: Iterable[OWLClassExpression]):
+        """
+        Args:
+            operands: Class expressions.
+        """
+        self._operands = tuple(operands)
+
+    def operands(self) -> Iterable[OWLClassExpression]:
+        # documented in parent
+        yield from self._operands
+
+    def __repr__(self):
+        return f'{type(self).__name__}({repr(self._operands)})'
+
+    def __eq__(self, other):
+        if type(other) == type(self):
+            return self._operands == other._operands
+        return NotImplemented
+
+    def __hash__(self):
+        return hash(self._operands)
+
+
+
+
+class OWLObjectUnionOf(OWLNaryBooleanClassExpression):
+    """Represents an ObjectUnionOf class expression in the OWL 2 Specification."""
+    __slots__ = '_operands'
+    type_index: Final = 3002
+
+    _operands: Sequence[OWLClassExpression]
+
+
+class OWLObjectIntersectionOf(OWLNaryBooleanClassExpression):
+    """Represents an OWLObjectIntersectionOf class expression in the OWL 2 Specification."""
+    __slots__ = '_operands'
+    type_index: Final = 3001
+
+    _operands: Sequence[OWLClassExpression]
diff --git a/owlapy/class_expression/owl_class.py b/owlapy/class_expression/owl_class.py
new file mode 100644
index 00000000..208f585d
--- /dev/null
+++ b/owlapy/class_expression/owl_class.py
@@ -0,0 +1,57 @@
+from .class_expression import OWLClassExpression, OWLObjectComplementOf
+from ..owlobject import OWLObject, OWLEntity
+from typing import Final, Union
+from ..iri import IRI
+
+
+class OWLClass(OWLClassExpression, OWLEntity):
+    """An OWL 2 named Class"""
+    __slots__ = '_iri', '_is_nothing', '_is_thing'
+    type_index: Final = 1001
+
+    _iri: 'IRI'
+    _is_nothing: bool
+    _is_thing: bool
+
+    def __init__(self, iri: Union[IRI, str]):
+        """Gets an instance of OWLClass that has the specified IRI.
+
+        Args:
+            iri:
+        """
+        if isinstance(iri, IRI):
+            self._iri = iri
+        else:
+            self._iri = IRI.create(iri)
+
+        self._is_nothing = self._iri.is_nothing()
+        self._is_thing = self._iri.is_thing()
+
+    def get_iri(self) -> 'IRI':
+        # documented in parent
+        return self._iri
+
+    def is_owl_thing(self) -> bool:
+        # documented in parent
+        return self._is_thing
+
+    def is_owl_nothing(self) -> bool:
+        # documented in parent
+        return self._is_nothing
+
+    def get_object_complement_of(self) -> OWLObjectComplementOf:
+        # documented in parent
+        return OWLObjectComplementOf(self)
+
+    def get_nnf(self) -> 'OWLClass':
+        # documented in parent
+        return self
+
+    @property
+    def str(self):
+        return self.get_iri().as_str()
+
+    @property
+    def reminder(self) -> str:
+        """The reminder of the IRI """
+        return self.get_iri().get_remainder()
diff --git a/owlapy/class_expression/restriction.py b/owlapy/class_expression/restriction.py
new file mode 100644
index 00000000..24f2c8d4
--- /dev/null
+++ b/owlapy/class_expression/restriction.py
@@ -0,0 +1,731 @@
+from abc import ABCMeta, abstractmethod
+from ..meta_classes import HasFiller, HasCardinality, HasOperands
+from typing import TypeVar, Generic, Final, Sequence, Union, Iterable
+from .nary_boolean_expression import OWLObjectIntersectionOf
+from .class_expression import OWLAnonymousClassExpression, OWLClassExpression
+from ..owl_property import OWLPropertyExpression, OWLObjectPropertyExpression, OWLDataPropertyExpression
+from ..data_ranges import OWLPropertyRange, OWLDataRange
+from ..owl_literal import OWLLiteral
+from ..owl_individual import OWLIndividual
+from ..types import OWLDatatype
+from ..owlobject import OWLObject
+from owlapy.vocab import OWLRDFVocabulary, XSDVocabulary, OWLFacet
+from datetime import datetime, date
+from pandas import Timedelta
+
+_T = TypeVar('_T')  #:
+_F = TypeVar('_F', bound=OWLPropertyRange)  #:
+
+Literals = Union['OWLLiteral', int, float, bool, Timedelta, datetime, date, str]  #:
+
+class OWLRestriction(OWLAnonymousClassExpression):
+    """Represents an Object Property Restriction or Data Property Restriction in the OWL 2 specification."""
+    __slots__ = ()
+
+    @abstractmethod
+    def get_property(self) -> OWLPropertyExpression:
+        """
+        Returns:
+            Property being restricted.
+        """
+        pass
+
+    def is_data_restriction(self) -> bool:
+        """Determines if this is a data restriction.
+
+        Returns:
+            True if this is a data restriction.
+        """
+        return False
+
+    def is_object_restriction(self) -> bool:
+        """Determines if this is an object restriction.
+
+        Returns:
+            True if this is an object restriction.
+        """
+        return False
+class OWLDataRestriction(OWLRestriction, metaclass=ABCMeta):
+    """Represents a Data Property Restriction in the OWL 2 specification."""
+    __slots__ = ()
+
+    def is_data_restriction(self) -> bool:
+        # documented in parent
+        return True
+
+    pass
+class OWLObjectRestriction(OWLRestriction, metaclass=ABCMeta):
+    """Represents a Object Property Restriction in the OWL 2 specification."""
+    __slots__ = ()
+
+    def is_object_restriction(self) -> bool:
+        # documented in parent
+        return True
+
+    @abstractmethod
+    def get_property(self) -> OWLObjectPropertyExpression:
+        # documented in parent
+        pass
+class OWLHasValueRestriction(Generic[_T], OWLRestriction, HasFiller[_T], metaclass=ABCMeta):
+    """OWLHasValueRestriction.
+
+    Args:
+        _T: The value type.
+    """
+    __slots__ = ()
+
+    _v: _T
+
+    def __init__(self, value: _T):
+        self._v = value
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._v == other._v
+        return NotImplemented
+
+    def __hash__(self):
+        return hash(self._v)
+
+    def get_filler(self) -> _T:
+        # documented in parent
+        return self._v
+class OWLQuantifiedRestriction(Generic[_T], OWLRestriction, HasFiller[_T], metaclass=ABCMeta):
+    """Represents a quantified restriction.
+
+    Args:
+        _T: value type
+    """
+    __slots__ = ()
+    pass
+class OWLQuantifiedObjectRestriction(OWLQuantifiedRestriction[OWLClassExpression], OWLObjectRestriction,
+                                     metaclass=ABCMeta):
+    """Represents a quantified object restriction."""
+    __slots__ = ()
+
+    _filler: OWLClassExpression
+
+    def __init__(self, filler: OWLClassExpression):
+        self._filler = filler
+
+    def get_filler(self) -> OWLClassExpression:
+        # documented in parent (HasFiller)
+        return self._filler
+class OWLObjectSomeValuesFrom(OWLQuantifiedObjectRestriction):
+    """Represents an ObjectSomeValuesFrom class expression in the OWL 2 Specification."""
+    __slots__ = '_property', '_filler'
+    type_index: Final = 3005
+
+    def __init__(self, property: OWLObjectPropertyExpression, filler: OWLClassExpression):
+        """Gets an OWLObjectSomeValuesFrom restriction.
+
+        Args:
+            property: The object property that the restriction acts along.
+            filler: The class expression that is the filler.
+
+        Returns:
+            An OWLObjectSomeValuesFrom restriction along the specified property with the specified filler.
+        """
+        super().__init__(filler)
+        self._property = property
+
+    def __repr__(self):
+        return f"OWLObjectSomeValuesFrom(property={repr(self._property)},filler={repr(self._filler)})"
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._filler == other._filler and self._property == other._property
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._filler, self._property))
+
+    def get_property(self) -> OWLObjectPropertyExpression:
+        # documented in parent
+        return self._property
+class OWLObjectAllValuesFrom(OWLQuantifiedObjectRestriction):
+    """Represents an ObjectAllValuesFrom class expression in the OWL 2 Specification."""
+    __slots__ = '_property', '_filler'
+    type_index: Final = 3006
+
+    def __init__(self, property: OWLObjectPropertyExpression, filler: OWLClassExpression):
+        super().__init__(filler)
+        self._property = property
+
+    def __repr__(self):
+        return f"OWLObjectAllValuesFrom(property={repr(self._property)},filler={repr(self._filler)})"
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._filler == other._filler and self._property == other._property
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._filler, self._property))
+
+    def get_property(self) -> OWLObjectPropertyExpression:
+        # documented in parent
+        return self._property
+
+class OWLCardinalityRestriction(Generic[_F], OWLQuantifiedRestriction[_F], HasCardinality, metaclass=ABCMeta):
+    """Base interface for owl min and max cardinality restriction.
+
+    Args:
+        _F: Type of filler.
+    """
+    __slots__ = ()
+
+    _cardinality: int
+    _filler: _F
+
+    def __init__(self, cardinality: int, filler: _F):
+        self._cardinality = cardinality
+        self._filler = filler
+
+    def get_cardinality(self) -> int:
+        # documented in parent
+        return self._cardinality
+
+    def get_filler(self) -> _F:
+        # documented in parent
+        return self._filler
+
+
+class OWLObjectCardinalityRestriction(OWLCardinalityRestriction[OWLClassExpression], OWLQuantifiedObjectRestriction):
+    """Represents Object Property Cardinality Restrictions in the OWL 2 specification."""
+    __slots__ = ()
+
+    _property: OWLObjectPropertyExpression
+
+    @abstractmethod
+    def __init__(self, cardinality: int, property: OWLObjectPropertyExpression, filler: OWLClassExpression):
+        super().__init__(cardinality, filler)
+        self._property = property
+
+    def get_property(self) -> OWLObjectPropertyExpression:
+        # documented in parent
+        return self._property
+
+    def __repr__(self):
+        return f"{type(self).__name__}(" \
+               f"property={repr(self.get_property())},{self.get_cardinality()},filler={repr(self.get_filler())})"
+
+    def __eq__(self, other):
+        if type(other) == type(self):
+            return self._property == other._property \
+                and self._cardinality == other._cardinality \
+                and self._filler == other._filler
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._property, self._cardinality, self._filler))
+
+class OWLObjectMinCardinality(OWLObjectCardinalityRestriction):
+    """Represents a ObjectMinCardinality restriction in the OWL 2 Specification."""
+    __slots__ = '_cardinality', '_filler', '_property'
+    type_index: Final = 3008
+
+    def __init__(self, cardinality: int, property: OWLObjectPropertyExpression, filler: OWLClassExpression):
+        """
+        Args:
+            cardinality: Cannot be negative.
+            property: The property that the restriction acts along.
+            filler: Class expression for restriction.
+
+        Returns:
+            An ObjectMinCardinality on the specified property.
+        """
+        super().__init__(cardinality, property, filler)
+class OWLObjectMaxCardinality(OWLObjectCardinalityRestriction):
+    """Represents a ObjectMaxCardinality restriction in the OWL 2 Specification."""
+    __slots__ = '_cardinality', '_filler', '_property'
+    type_index: Final = 3010
+
+    def __init__(self, cardinality: int, property: OWLObjectPropertyExpression, filler: OWLClassExpression):
+        """
+        Args:
+            cardinality: Cannot be negative.
+            property: The property that the restriction acts along.
+            filler: Class expression for restriction.
+
+        Returns:
+            An ObjectMaxCardinality on the specified property.
+        """
+        super().__init__(cardinality, property, filler)
+class OWLObjectExactCardinality(OWLObjectCardinalityRestriction):
+    """Represents an ObjectExactCardinality  restriction in the OWL 2 Specification."""
+    __slots__ = '_cardinality', '_filler', '_property'
+    type_index: Final = 3009
+
+    def __init__(self, cardinality: int, property: OWLObjectPropertyExpression, filler: OWLClassExpression):
+        """
+        Args:
+            cardinality: Cannot be negative.
+            property: The property that the restriction acts along.
+            filler: Class expression for restriction.
+
+        Returns:
+            An ObjectExactCardinality on the specified property.
+        """
+        super().__init__(cardinality, property, filler)
+
+    def as_intersection_of_min_max(self) -> OWLObjectIntersectionOf:
+        """Obtains an equivalent form that is a conjunction of a min cardinality and max cardinality restriction.
+
+        Returns:
+            The semantically equivalent but structurally simpler form (= 1 R C) = >= 1 R C and <= 1 R C.
+        """
+        args = self.get_cardinality(), self.get_property(), self.get_filler()
+        return OWLObjectIntersectionOf((OWLObjectMinCardinality(*args), OWLObjectMaxCardinality(*args)))
+class OWLObjectHasSelf(OWLObjectRestriction):
+    """Represents an ObjectHasSelf class expression in the OWL 2 Specification."""
+    __slots__ = '_property'
+    type_index: Final = 3011
+
+    _property: OWLObjectPropertyExpression
+
+    def __init__(self, property: OWLObjectPropertyExpression):
+        """Object has self restriction
+
+        Args:
+            property: The property that the restriction acts along.
+
+        Returns:
+            A ObjectHasSelf class expression on the specified property.
+        """
+        self._property = property
+
+    def get_property(self) -> OWLObjectPropertyExpression:
+        # documented in parent
+        return self._property
+
+    def __eq__(self, other):
+        if type(other) == type(self):
+            return self._property == other._property
+        return NotImplemented
+
+    def __hash__(self):
+        return hash(self._property)
+
+    def __repr__(self):
+        return f'OWLObjectHasSelf({self._property})'
+
+
+
+class OWLQuantifiedDataRestriction(OWLQuantifiedRestriction[OWLDataRange],
+                                   OWLDataRestriction, metaclass=ABCMeta):
+    """Represents a quantified data restriction."""
+    __slots__ = ()
+
+    _filler: OWLDataRange
+
+    def __init__(self, filler: OWLDataRange):
+        self._filler = filler
+
+    def get_filler(self) -> OWLDataRange:
+        # documented in parent (HasFiller)
+        return self._filler
+class OWLDataAllValuesFrom(OWLQuantifiedDataRestriction):
+    """Represents DataAllValuesFrom class expressions in the OWL 2 Specification."""
+    __slots__ = '_property'
+
+    type_index: Final = 3013
+
+    _property: OWLDataPropertyExpression
+
+    def __init__(self, property: OWLDataPropertyExpression, filler: OWLDataRange):
+        """Gets an OWLDataAllValuesFrom restriction.
+
+        Args:
+            property: The data property that the restriction acts along.
+            filler: The data range that is the filler.
+
+        Returns:
+            An OWLDataAllValuesFrom restriction along the specified property with the specified filler.
+        """
+        super().__init__(filler)
+        self._property = property
+
+    def __repr__(self):
+        return f"OWLDataAllValuesFrom(property={repr(self._property)},filler={repr(self._filler)})"
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._filler == other._filler and self._property == other._property
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._filler, self._property))
+
+    def get_property(self) -> OWLDataPropertyExpression:
+        # documented in parent
+        return self._property
+
+
+
+class OWLDataCardinalityRestriction(OWLCardinalityRestriction[OWLDataRange],
+                                    OWLQuantifiedDataRestriction,
+                                    OWLDataRestriction, metaclass=ABCMeta):
+    """Represents Data Property Cardinality Restrictions in the OWL 2 specification."""
+    __slots__ = ()
+
+    _property: OWLDataPropertyExpression
+
+    @abstractmethod
+    def __init__(self, cardinality: int, property: OWLDataPropertyExpression, filler: OWLDataRange):
+        super().__init__(cardinality, filler)
+        self._property = property
+
+    def get_property(self) -> OWLDataPropertyExpression:
+        # documented in parent
+        return self._property
+
+    def __repr__(self):
+        return f"{type(self).__name__}(" \
+               f"property={repr(self.get_property())},{self.get_cardinality()},filler={repr(self.get_filler())})"
+
+    def __eq__(self, other):
+        if type(other) == type(self):
+            return self._property == other._property \
+                and self._cardinality == other._cardinality \
+                and self._filler == other._filler
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._property, self._cardinality, self._filler))
+
+
+
+class OWLDataExactCardinality(OWLDataCardinalityRestriction):
+    """Represents DataExactCardinality restrictions in the OWL 2 Specification."""
+    __slots__ = '_cardinality', '_filler', '_property'
+
+    type_index: Final = 3016
+
+    def __init__(self, cardinality: int, property: OWLDataPropertyExpression, filler: OWLDataRange):
+        """
+        Args:
+            cardinality: Cannot be negative.
+            property: The property that the restriction acts along.
+            filler: Data range for restriction
+
+        Returns:
+            A DataExactCardinality on the specified property.
+        """
+        super().__init__(cardinality, property, filler)
+
+    def as_intersection_of_min_max(self) -> OWLObjectIntersectionOf:
+        """Obtains an equivalent form that is a conjunction of a min cardinality and max cardinality restriction.
+
+        Returns:
+            The semantically equivalent but structurally simpler form (= 1 R D) = >= 1 R D and <= 1 R D.
+        """
+        args = self.get_cardinality(), self.get_property(), self.get_filler()
+        return OWLObjectIntersectionOf((OWLDataMinCardinality(*args), OWLDataMaxCardinality(*args)))
+
+class OWLDataMaxCardinality(OWLDataCardinalityRestriction):
+    """Represents DataMaxCardinality restrictions in the OWL 2 Specification."""
+    __slots__ = '_cardinality', '_filler', '_property'
+
+    type_index: Final = 3017
+
+    def __init__(self, cardinality: int, property: OWLDataPropertyExpression, filler: OWLDataRange):
+        """
+        Args:
+            cardinality: Cannot be negative.
+            property: The property that the restriction acts along.
+            filler: Data range for restriction.
+
+        Returns:
+            A DataMaxCardinality on the specified property.
+        """
+        super().__init__(cardinality, property, filler)
+class OWLDataMinCardinality(OWLDataCardinalityRestriction):
+    """Represents DataMinCardinality restrictions in the OWL 2 Specification."""
+    __slots__ = '_cardinality', '_filler', '_property'
+
+    type_index: Final = 3015
+
+    def __init__(self, cardinality: int, property: OWLDataPropertyExpression, filler: OWLDataRange):
+        """
+        Args:
+            cardinality: Cannot be negative.
+            property: The property that the restriction acts along.
+            filler: Data range for restriction.
+
+        Returns:
+            A DataMinCardinality on the specified property.
+        """
+        super().__init__(cardinality, property, filler)
+
+
+class OWLDataSomeValuesFrom(OWLQuantifiedDataRestriction):
+    """Represents a DataSomeValuesFrom restriction in the OWL 2 Specification."""
+    __slots__ = '_property'
+
+    type_index: Final = 3012
+
+    _property: OWLDataPropertyExpression
+
+    def __init__(self, property: OWLDataPropertyExpression, filler: OWLDataRange):
+        """Gets an OWLDataSomeValuesFrom restriction.
+
+        Args:
+            property: The data property that the restriction acts along.
+            filler: The data range that is the filler.
+
+        Returns:
+            An OWLDataSomeValuesFrom restriction along the specified property with the specified filler.
+        """
+        super().__init__(filler)
+        self._property = property
+
+    def __repr__(self):
+        return f"OWLDataSomeValuesFrom(property={repr(self._property)},filler={repr(self._filler)})"
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._filler == other._filler and self._property == other._property
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._filler, self._property))
+
+    def get_property(self) -> OWLDataPropertyExpression:
+        # documented in parent
+        return self._property
+
+class OWLDataHasValue(OWLHasValueRestriction[OWLLiteral], OWLDataRestriction):
+    """Represents DataHasValue restrictions in the OWL 2 Specification."""
+    __slots__ = '_property'
+
+    type_index: Final = 3014
+
+    _property: OWLDataPropertyExpression
+
+    def __init__(self, property: OWLDataPropertyExpression, value: OWLLiteral):
+        """Gets an OWLDataHasValue restriction.
+
+        Args:
+            property: The data property that the restriction acts along.
+            filler: The literal value.
+
+        Returns:
+            An OWLDataHasValue restriction along the specified property with the specified literal.
+        """
+        super().__init__(value)
+        self._property = property
+
+    def __repr__(self):
+        return f"OWLDataHasValue(property={repr(self._property)},value={repr(self._v)})"
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._v == other._v and self._property == other._property
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._v, self._property))
+
+    def as_some_values_from(self) -> OWLClassExpression:
+        """A convenience method that obtains this restriction as an existential restriction with a nominal filler.
+
+        Returns:
+            The existential equivalent of this value restriction. simp(HasValue(p a)) = some(p {a}).
+        """
+        return OWLDataSomeValuesFrom(self.get_property(), OWLDataOneOf(self.get_filler()))
+
+    def get_property(self) -> OWLDataPropertyExpression:
+        # documented in parent
+        return self._property
+
+
+
+class OWLObjectOneOf(OWLAnonymousClassExpression, HasOperands[OWLIndividual]):
+    """Represents an ObjectOneOf class expression in the OWL 2 Specification."""
+    __slots__ = '_values'
+    type_index: Final = 3004
+
+    def __init__(self, values: Union[OWLIndividual, Iterable[OWLIndividual]]):
+        if isinstance(values, OWLIndividual):
+            self._values = values,
+        else:
+            for _ in values:
+                assert isinstance(_, OWLIndividual)
+            self._values = tuple(values)
+
+    def individuals(self) -> Iterable[OWLIndividual]:
+        """Gets the individuals that are in the oneOf. These individuals represent the exact instances (extension)
+         of this class expression.
+
+         Returns:
+             The individuals that are the values of this {@code ObjectOneOf} class expression.
+        """
+        yield from self._values
+
+    def operands(self) -> Iterable[OWLIndividual]:
+        # documented in parent
+        yield from self.individuals()
+
+    def as_object_union_of(self) -> OWLClassExpression:
+        """Simplifies this enumeration to a union of singleton nominals.
+
+        Returns:
+            This enumeration in a more standard DL form.
+            simp({a}) = {a} simp({a0, ... , {an}) = unionOf({a0}, ... , {an})
+        """
+        if len(self._values) == 1:
+            return self
+        return OWLObjectUnionOf(map(lambda _: OWLObjectOneOf(_), self.individuals()))
+
+    def __hash__(self):
+        return hash(self._values)
+
+    def __eq__(self, other):
+        if type(other) == type(self):
+            return self._values == other._values
+        return NotImplemented
+
+    def __repr__(self):
+        return f'OWLObjectOneOf({self._values})'
+
+
+class OWLDataOneOf(OWLDataRange, HasOperands[OWLLiteral]):
+    """Represents DataOneOf in the OWL 2 Specification."""
+    type_index: Final = 4003
+
+    _values: Sequence[OWLLiteral]
+
+    def __init__(self, values: Union[OWLLiteral, Iterable[OWLLiteral]]):
+        if isinstance(values, OWLLiteral):
+            self._values = values,
+        else:
+            for _ in values:
+                assert isinstance(_, OWLLiteral)
+            self._values = tuple(values)
+
+    def values(self) -> Iterable[OWLLiteral]:
+        """Gets the values that are in the oneOf.
+
+         Returns:
+             The values of this {@code DataOneOf} class expression.
+        """
+        yield from self._values
+
+    def operands(self) -> Iterable[OWLLiteral]:
+        # documented in parent
+        yield from self.values()
+
+    def __hash__(self):
+        return hash(self._values)
+
+    def __eq__(self, other):
+        if type(other) == type(self):
+            return self._values == other._values
+        return NotImplemented
+
+    def __repr__(self):
+        return f'OWLDataOneOf({self._values})'
+
+
+class OWLObjectHasValue(OWLHasValueRestriction[OWLIndividual], OWLObjectRestriction):
+    """Represents an ObjectHasValue class expression in the OWL 2 Specification."""
+    __slots__ = '_property', '_v'
+    type_index: Final = 3007
+
+    _property: OWLObjectPropertyExpression
+    _v: OWLIndividual
+
+    def __init__(self, property: OWLObjectPropertyExpression, individual: OWLIndividual):
+        """
+        Args:
+            property: The property that the restriction acts along.
+            individual: Individual for restriction.
+
+        Returns:
+            A HasValue restriction with specified property and value
+        """
+        super().__init__(individual)
+        self._property = property
+
+    def get_property(self) -> OWLObjectPropertyExpression:
+        # documented in parent
+        return self._property
+
+    def as_some_values_from(self) -> OWLClassExpression:
+        """A convenience method that obtains this restriction as an existential restriction with a nominal filler.
+
+        Returns:
+            The existential equivalent of this value restriction. simp(HasValue(p a)) = some(p {a}).
+        """
+        return OWLObjectSomeValuesFrom(self.get_property(), OWLObjectOneOf(self.get_filler()))
+
+    def __repr__(self):
+        return f'OWLObjectHasValue(property={self.get_property()}, individual={self._v})'
+class OWLDatatypeRestriction(OWLDataRange):
+    """Represents a DatatypeRestriction data range in the OWL 2 Specification."""
+    __slots__ = '_type', '_facet_restrictions'
+
+    type_index: Final = 4006
+
+    _type: OWLDatatype
+    _facet_restrictions: Sequence['OWLFacetRestriction']
+
+    def __init__(self, type_: OWLDatatype, facet_restrictions: Union['OWLFacetRestriction',
+    Iterable['OWLFacetRestriction']]):
+        self._type = type_
+        if isinstance(facet_restrictions, OWLFacetRestriction):
+            facet_restrictions = facet_restrictions,
+        self._facet_restrictions = tuple(facet_restrictions)
+
+    def get_datatype(self) -> OWLDatatype:
+        return self._type
+
+    def get_facet_restrictions(self) -> Sequence['OWLFacetRestriction']:
+        return self._facet_restrictions
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._type == other._type \
+                and self._facet_restrictions == other._facet_restrictions
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._type, self._facet_restrictions))
+
+    def __repr__(self):
+        return f'OWLDatatypeRestriction({repr(self._type)}, {repr(self._facet_restrictions)})'
+class OWLFacetRestriction(OWLObject):
+    """A facet restriction is used to restrict a particular datatype."""
+
+    __slots__ = '_facet', '_literal'
+
+    type_index: Final = 4007
+
+    _facet: OWLFacet
+    _literal: 'OWLLiteral'
+
+    def __init__(self, facet: OWLFacet, literal: Literals):
+        self._facet = facet
+        if isinstance(literal, OWLLiteral):
+            self._literal = literal
+        else:
+            self._literal = OWLLiteral(literal)
+
+    def get_facet(self) -> OWLFacet:
+        return self._facet
+
+    def get_facet_value(self) -> 'OWLLiteral':
+        return self._literal
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._facet == other._facet and self._literal == other._literal
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._facet, self._literal))
+
+    def __repr__(self):
+        return f'OWLFacetRestriction({self._facet}, {repr(self._literal)})'
+
diff --git a/owlapy/data_ranges/__init__.py b/owlapy/data_ranges/__init__.py
new file mode 100644
index 00000000..885570b1
--- /dev/null
+++ b/owlapy/data_ranges/__init__.py
@@ -0,0 +1,95 @@
+"""https://www.w3.org/TR/owl2-syntax/#Data_Ranges
+
+DataRange := Datatype | DataIntersectionOf | DataUnionOf | DataComplementOf | DataOneOf | DatatypeRestriction
+"""
+from abc import abstractmethod, ABCMeta
+from ..owlobject import OWLObject, OWLEntity
+from ..meta_classes import HasOperands
+from typing import Final, Iterable, Sequence
+# from ..owl_literal import OWLLiteral
+from typing import Final, Sequence, Union, Iterable
+from ..iri import IRI
+
+from abc import ABCMeta
+
+class OWLPropertyRange(OWLObject, metaclass=ABCMeta):
+    """OWL Objects that can be the ranges of properties."""
+
+
+class OWLDataRange(OWLPropertyRange, metaclass=ABCMeta):
+    """Represents a DataRange in the OWL 2 Specification."""
+
+
+class OWLDataComplementOf(OWLDataRange):
+    """Represents DataComplementOf in the OWL 2 Specification."""
+    type_index: Final = 4002
+
+    _data_range: OWLDataRange
+
+    def __init__(self, data_range: OWLDataRange):
+        """
+        Args:
+            data_range: Data range to complement.
+        """
+        self._data_range = data_range
+
+    def get_data_range(self) -> OWLDataRange:
+        """
+        Returns:
+            The wrapped data range.
+        """
+        return self._data_range
+
+    def __repr__(self):
+        return f"OWLDataComplementOf({repr(self._data_range)})"
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._data_range == other._data_range
+        return NotImplemented
+
+    def __hash__(self):
+        return hash(self._data_range)
+
+class OWLNaryDataRange(OWLDataRange, HasOperands[OWLDataRange]):
+    """OWLNaryDataRange."""
+    __slots__ = ()
+
+    _operands: Sequence[OWLDataRange]
+
+    def __init__(self, operands: Iterable[OWLDataRange]):
+        """
+        Args:
+            operands: Data ranges.
+        """
+        self._operands = tuple(operands)
+
+    def operands(self) -> Iterable[OWLDataRange]:
+        # documented in parent
+        yield from self._operands
+
+    def __repr__(self):
+        return f'{type(self).__name__}({repr(self._operands)})'
+
+    def __eq__(self, other):
+        if type(other) == type(self):
+            return self._operands == other._operands
+        return NotImplemented
+
+    def __hash__(self):
+        return hash(self._operands)
+
+class OWLDataUnionOf(OWLNaryDataRange):
+    """Represents a DataUnionOf data range in the OWL 2 Specification."""
+    __slots__ = '_operands'
+    type_index: Final = 4005
+
+    _operands: Sequence[OWLDataRange]
+
+class OWLDataIntersectionOf(OWLNaryDataRange):
+    """Represents DataIntersectionOf  in the OWL 2 Specification."""
+    __slots__ = '_operands'
+    type_index: Final = 4004
+
+    _operands: Sequence[OWLDataRange]
+
diff --git a/owlapy/entities/__init__.py b/owlapy/entities/__init__.py
new file mode 100644
index 00000000..3af707be
--- /dev/null
+++ b/owlapy/entities/__init__.py
@@ -0,0 +1,8 @@
+# https://www.w3.org/TR/owl2-syntax/#Entities.2C_Literals.2C_and_Anonymous_Individuals
+"""
+Entities are the fundamental building blocks of OWL 2 ontologies, and they define the vocabulary — the named terms — of an ontology.
+In logic, the set of entities is usually said to constitute the signature of an ontology.
+
+Classes, datatypes, object properties, data properties, annotation properties, and named individuals
+are entities, and they are all uniquely identified by an IR.
+"""
diff --git a/owlapy/has.py b/owlapy/has.py
new file mode 100644
index 00000000..dfaca01d
--- /dev/null
+++ b/owlapy/has.py
@@ -0,0 +1,7 @@
+from typing import Protocol, ClassVar
+class HasIndex(Protocol):
+    """Interface for types with an index; this is used to group objects by type when sorting."""
+    type_index: ClassVar[int]  #: index for this type. This is a sorting index for the types.
+
+    def __eq__(self, other): ...
+
diff --git a/owlapy/io.py b/owlapy/io.py
deleted file mode 100644
index 61b45c21..00000000
--- a/owlapy/io.py
+++ /dev/null
@@ -1,43 +0,0 @@
-"""Abstract renderer and parser classes."""
-from abc import abstractmethod, ABCMeta
-
-from owlapy.model import OWLObject
-
-
-class OWLObjectRenderer(metaclass=ABCMeta):
-    """Abstract class with a render method to render an OWL Object into a string."""
-    @abstractmethod
-    def set_short_form_provider(self, short_form_provider) -> None:
-        """Configure a short form provider that shortens the OWL objects during rendering.
-
-        Args:
-            short_form_provider: Short form provider.
-        """
-        pass
-
-    @abstractmethod
-    def render(self, o: OWLObject) -> str:
-        """Render OWL Object to string.
-
-        Args:
-            o: OWL Object.
-
-        Returns:
-            String rendition of OWL object.
-        """
-        pass
-
-
-class OWLObjectParser(metaclass=ABCMeta):
-    """Abstract class with a parse method to parse a string to an OWL Object."""
-    @abstractmethod
-    def parse_expression(self, expression_str: str) -> OWLObject:
-        """Parse a string to an OWL Object.
-
-        Args:
-            expression_str (str): Expression string.
-
-        Returns:
-            The OWL Object which is represented by the string.
-        """
-        pass
diff --git a/owlapy/model/_iri.py b/owlapy/iri.py
similarity index 93%
rename from owlapy/model/_iri.py
rename to owlapy/iri.py
index 9fe98744..84434ce8 100644
--- a/owlapy/model/_iri.py
+++ b/owlapy/iri.py
@@ -4,24 +4,10 @@
 from weakref import WeakKeyDictionary
 
 from owlapy import namespaces
-from owlapy.model._base import OWLAnnotationSubject, OWLAnnotationValue
+from .owl_annotation import OWLAnnotationSubject, OWLAnnotationValue
 from owlapy.namespaces import Namespaces
 
 
-class HasIRI(metaclass=ABCMeta):
-    """Simple class to access the IRI."""
-    __slots__ = ()
-
-    @abstractmethod
-    def get_iri(self) -> 'IRI':
-        """Gets the IRI of this object.
-
-        Returns:
-            The IRI of this object.
-        """
-        pass
-
-
 class _WeakCached(type):
     __slots__ = ()
 
diff --git a/owlapy/meta_classes.py b/owlapy/meta_classes.py
new file mode 100644
index 00000000..bcf73579
--- /dev/null
+++ b/owlapy/meta_classes.py
@@ -0,0 +1,72 @@
+# https://docs.python.org/3/reference/datamodel.html#metaclasses
+from typing import TypeVar, Generic, Iterable
+from abc import ABCMeta, abstractmethod
+
+_T = TypeVar('_T')  #:
+
+
+class HasIRI(metaclass=ABCMeta):
+    """Simple class to access the IRI."""
+    __slots__ = ()
+
+    @abstractmethod
+    def get_iri(self) -> 'IRI':
+        """Gets the IRI of this object.
+
+        Returns:
+            The IRI of this object.
+        """
+        pass
+
+
+class HasOperands(Generic[_T], metaclass=ABCMeta):
+    """An interface to objects that have a collection of operands.
+
+    Args:
+        _T: Operand type.
+    """
+    __slots__ = ()
+
+    @abstractmethod
+    def operands(self) -> Iterable[_T]:
+        """Gets the operands - e.g., the individuals in a sameAs axiom, or the classes in an equivalent
+        classes axiom.
+
+        Returns:
+            The operands.
+        """
+        pass
+
+
+class HasFiller(Generic[_T], metaclass=ABCMeta):
+    """An interface to objects that have a filler.
+
+    Args:
+        _T: Filler type.
+    """
+    __slots__ = ()
+
+    @abstractmethod
+    def get_filler(self) -> _T:
+        """Gets the filler for this restriction. In the case of an object restriction this will be an individual, in
+        the case of a data restriction this will be a constant (data value). For quantified restriction this will be
+        a class expression or a data range.
+
+        Returns:
+            the value
+        """
+        pass
+
+
+class HasCardinality(metaclass=ABCMeta):
+    """An interface to objects that have a cardinality."""
+    __slots__ = ()
+
+    @abstractmethod
+    def get_cardinality(self) -> int:
+        """Gets the cardinality of a restriction.
+
+        Returns:
+            The cardinality. A non-negative integer.
+        """
+        pass
diff --git a/owlapy/model/__init__.py b/owlapy/model/__init__.py
index 707d6247..ac96a886 100644
--- a/owlapy/model/__init__.py
+++ b/owlapy/model/__init__.py
@@ -1,16 +1,38 @@
-"""@TODO: CD: This is not a python code. We should refactor this model module."""
-
 from abc import ABCMeta, abstractmethod
-from functools import total_ordering
-from itertools import combinations
 from typing import Generic, Iterable, Sequence, Set, TypeVar, Union, Final, Optional, Protocol, ClassVar, List
-from pandas import Timedelta
 from datetime import datetime, date
-
+from pandas import Timedelta
 from owlapy.vocab import OWLRDFVocabulary, XSDVocabulary, OWLFacet
 from owlapy._utils import MOVE
-from owlapy.model._base import OWLObject, OWLAnnotationObject, OWLAnnotationSubject, OWLAnnotationValue
-from owlapy.model._iri import HasIRI, IRI
+from owlapy.owlobject import OWLObject, OWLEntity
+from owlapy.owl_annotation import OWLAnnotationObject, OWLAnnotationSubject, OWLAnnotationValue
+from owlapy.iri import IRI
+from owlapy.has import HasIndex
+from owlapy.meta_classes import HasIRI, HasOperands, HasFiller, HasCardinality
+from owlapy.class_expression import OWLClassExpression, OWLNaryBooleanClassExpression, OWLObjectIntersectionOf, \
+    OWLObjectUnionOf, OWLObjectComplementOf
+from owlapy.class_expression import OWLThing, OWLNothing, OWLClass
+
+from owlapy.data_ranges import OWLPropertyRange, OWLDataRange
+
+from owlapy.owl_property import OWLObjectPropertyExpression, OWLProperty, OWLPropertyExpression, \
+    OWLDataPropertyExpression, OWLDataProperty, OWLObjectProperty
+from owlapy.class_expression import (OWLRestriction, OWLObjectAllValuesFrom, OWLObjectSomeValuesFrom,
+                                    OWLQuantifiedRestriction, OWLQuantifiedObjectRestriction,
+                                    OWLObjectRestriction, OWLHasValueRestriction, OWLDataRestriction,
+                                    OWLCardinalityRestriction, OWLObjectMinCardinality, OWLObjectCardinalityRestriction,
+                                    OWLDataAllValuesFrom,
+                                    OWLObjectHasSelf, OWLObjectMaxCardinality, OWLObjectExactCardinality,
+                                    OWLDataExactCardinality, OWLDataMinCardinality,
+                                    OWLDataMaxCardinality, OWLDataSomeValuesFrom, OWLDataHasValue, OWLDataOneOf,
+                                    OWLQuantifiedDataRestriction, OWLDataCardinalityRestriction)
+
+from owlapy.owl_individual import OWLNamedIndividual, OWLIndividual
+from owlapy.owl_axiom import (OWLEquivalentClassesAxiom, OWLClassAxiom,
+                              OWLDataPropertyDomainAxiom, OWLAxiom, OWLDataPropertyRangeAxiom,
+                              OWLObjectPropertyDomainAxiom, OWLObjectPropertyRangeAxiom)
+from owlapy.types import OWLDatatype
+from owlapy.owl_literal import OWLLiteral
 
 MOVE(OWLObject, OWLAnnotationObject, OWLAnnotationSubject, OWLAnnotationValue, HasIRI, IRI)
 
@@ -19,3060 +41,97 @@
 _P = TypeVar('_P', bound='OWLPropertyExpression')  #:
 _R = TypeVar('_R', bound='OWLPropertyRange')  #:
 Literals = Union['OWLLiteral', int, float, bool, Timedelta, datetime, date, str]  #:
+_M = TypeVar('_M', bound='OWLOntologyManager')  #:
 
 
-class HasIndex(Protocol):
-    """Interface for types with an index; this is used to group objects by type when sorting."""
-    type_index: ClassVar[int]  #: index for this type. This is a sorting index for the types.
-
-    def __eq__(self, other): ...
-
-
-class HasOperands(Generic[_T], metaclass=ABCMeta):
-    """An interface to objects that have a collection of operands.
-
-    Args:
-        _T: Operand type.
+class OWLOntologyID:
+    """An object that identifies an ontology. Since OWL 2, ontologies do not have to have an ontology IRI, or if they
+    have an ontology IRI then they can optionally also have a version IRI. Instances of this OWLOntologyID class bundle
+    identifying information of an ontology together. If an ontology doesn't have an ontology IRI then we say that it is
+    "anonymous".
     """
-    __slots__ = ()
-
-    @abstractmethod
-    def operands(self) -> Iterable[_T]:
-        """Gets the operands - e.g., the individuals in a sameAs axiom, or the classes in an equivalent
-        classes axiom.
-
-        Returns:
-            The operands.
-        """
-        pass
-
-
-class OWLPropertyRange(OWLObject, metaclass=ABCMeta):
-    """OWL Objects that can be the ranges of properties."""
-
-
-class OWLDataRange(OWLPropertyRange, metaclass=ABCMeta):
-    """Represents a DataRange in the OWL 2 Specification."""
-
-
-class OWLClassExpression(OWLPropertyRange):
-    """An OWL 2 Class Expression."""
-    __slots__ = ()
+    __slots__ = '_ontology_iri', '_version_iri'
 
-    @abstractmethod
-    def is_owl_thing(self) -> bool:
-        """Determines if this expression is the built in class owl:Thing. This method does not determine if the class
-        is equivalent to owl:Thing.
+    _ontology_iri: Optional[IRI]
+    _version_iri: Optional[IRI]
 
-        Returns:
-            True if this expression is owl:Thing.
-        """
-        pass
+    def __init__(self, ontology_iri: Optional[IRI] = None, version_iri: Optional[IRI] = None):
+        """Constructs an ontology identifier specifying the ontology IRI and version IRI.
 
-    @abstractmethod
-    def is_owl_nothing(self) -> bool:
-        """Determines if this expression is the built in class owl:Nothing. This method does not determine if the class
-        is equivalent to owl:Nothing.
+        Args:
+            ontology_iri: The ontology IRI (optional).
+            version_iri: The version IRI (must be None if no ontology_iri is provided).
         """
-        pass
+        self._ontology_iri = ontology_iri
+        self._version_iri = version_iri
 
-    @abstractmethod
-    def get_object_complement_of(self) -> 'OWLObjectComplementOf':
-        """Gets the object complement of this class expression.
+    def get_ontology_iri(self) -> Optional[IRI]:
+        """Gets the ontology IRI.
 
         Returns:
-            A class expression that is the complement of this class expression.
+            Ontology IRI. If the ontology is anonymous, it will return None.
         """
-        pass
+        return self._ontology_iri
 
-    @abstractmethod
-    def get_nnf(self) -> 'OWLClassExpression':
-        """Gets the negation normal form of the complement of this expression.
+    def get_version_iri(self) -> Optional[IRI]:
+        """Gets the version IRI.
 
         Returns:
-            A expression that represents the NNF of the complement of this expression.
+            Version IRI or None.
         """
-        pass
-
-
-class OWLAnonymousClassExpression(OWLClassExpression, metaclass=ABCMeta):
-    """A Class Expression which is not a named Class."""
-
-    def is_owl_nothing(self) -> bool:
-        # documented in parent
-        return False
-
-    def is_owl_thing(self) -> bool:
-        # documented in parent
-        return False
-
-    def get_object_complement_of(self) -> 'OWLObjectComplementOf':
-        # documented in parent
-        return OWLObjectComplementOf(self)
-
-    def get_nnf(self) -> 'OWLClassExpression':
-        # documented in parent
-        from owlapy.util import NNF
-        return NNF().get_class_nnf(self)
-
-
-class OWLBooleanClassExpression(OWLAnonymousClassExpression, metaclass=ABCMeta):
-    """Represent an anonymous boolean class expression."""
-    __slots__ = ()
-    pass
-
-
-class OWLObjectComplementOf(OWLBooleanClassExpression, HasOperands[OWLClassExpression]):
-    """Represents an ObjectComplementOf class expression in the OWL 2 Specification."""
-    __slots__ = '_operand'
-    type_index: Final = 3003
-
-    _operand: OWLClassExpression
+        return self._version_iri
 
-    def __init__(self, op: OWLClassExpression):
-        """
-        Args:
-            op: Class expression to complement.
-        """
-        self._operand = op
+    def get_default_document_iri(self) -> Optional[IRI]:
+        """Gets the IRI which is used as a default for the document that contain a representation of an ontology with
+        this ID. This will be the version IRI if there is an ontology IRI and version IRI, else it will be the ontology
+        IRI if there is an ontology IRI but no version IRI, else it will be None if there is no ontology IRI. See
+        Ontology Documents in the OWL 2 Structural Specification.
 
-    def get_operand(self) -> OWLClassExpression:
-        """
         Returns:
-            The wrapped expression.
+            the IRI that can be used as a default for an ontology document, or None.
         """
-        return self._operand
+        if self._ontology_iri is not None:
+            if self._version_iri is not None:
+                return self._version_iri
+        return self._ontology_iri
 
-    def operands(self) -> Iterable[OWLClassExpression]:
-        # documented in parent
-        yield self._operand
+    def is_anonymous(self) -> bool:
+        return self._ontology_iri is None
 
     def __repr__(self):
-        return f"OWLObjectComplementOf({repr(self._operand)})"
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._operand == other._operand
-        return NotImplemented
-
-    def __hash__(self):
-        return hash(self._operand)
-
-
-class OWLNamedObject(OWLObject, HasIRI, metaclass=ABCMeta):
-    """Represents a named object for example, class, property, ontology etc. - i.e. anything that has an
-     IRI as its name."""
-    __slots__ = ()
-
-    _iri: IRI
+        return f"OWLOntologyID({repr(self._ontology_iri)}, {repr(self._version_iri)})"
 
     def __eq__(self, other):
         if type(other) is type(self):
-            return self._iri == other._iri
-        return NotImplemented
-
-    def __lt__(self, other):
-        if type(other) is type(self):
-            return self._iri.as_str() < other._iri.as_str()
+            return self._ontology_iri == other._ontology_iri and self._version_iri == other._version_iri
         return NotImplemented
 
-    def __hash__(self):
-        return hash(self._iri)
-
-    def __repr__(self):
-        return f"{type(self).__name__}({repr(self._iri)})"
-
-    pass
-
-
-class OWLEntity(OWLNamedObject, metaclass=ABCMeta):
-    """Represents Entities in the OWL 2 Specification."""
-    __slots__ = ()
-
-    def to_string_id(self) -> str:
-        return self.get_iri().as_str()
-
-    def is_anonymous(self) -> bool:
-        return False
-
-    pass
-
-
-class OWLClass(OWLClassExpression, OWLEntity):
-    """An OWL 2 named Class"""
-    __slots__ = '_iri', '_is_nothing', '_is_thing'
-    type_index: Final = 1001
-
-    _iri: IRI
-    _is_nothing: bool
-    _is_thing: bool
-
-    def __init__(self, iri: IRI):
-        """Gets an instance of OWLClass that has the specified IRI.
-
-        Args:
-            iri: The IRI.
-        """
-        self._is_nothing = iri.is_nothing()
-        self._is_thing = iri.is_thing()
-        self._iri = iri
-
-    def get_iri(self) -> IRI:
-        # documented in parent
-        return self._iri
-
-    def is_owl_thing(self) -> bool:
-        # documented in parent
-        return self._is_thing
-
-    def is_owl_nothing(self) -> bool:
-        # documented in parent
-        return self._is_nothing
-
-    def get_object_complement_of(self) -> OWLObjectComplementOf:
-        # documented in parent
-        return OWLObjectComplementOf(self)
-
-    def get_nnf(self) -> 'OWLClass':
-        # documented in parent
-        return self
-
-    @property
-    def str(self):
-        return self.get_iri().as_str()
-
-    @property
-    def reminder(self)->str:
-        """The reminder of the IRI """
-        return self.get_iri().get_remainder()
-
-class OWLPropertyExpression(OWLObject, metaclass=ABCMeta):
-    """Represents a property or possibly the inverse of a property."""
-    __slots__ = ()
-
-    def is_data_property_expression(self) -> bool:
-        """
-        Returns:
-            True if this is a data property.
-        """
-        return False
-
-    def is_object_property_expression(self) -> bool:
-        """
-        Returns:
-            True if this is an object property.
-        """
-        return False
-
-    def is_owl_top_object_property(self) -> bool:
-        """Determines if this is the owl:topObjectProperty.
-
-        Returns:
-            True if this property is the owl:topObjectProperty.
-        """
-        return False
-
-    def is_owl_top_data_property(self) -> bool:
-        """Determines if this is the owl:topDataProperty.
-
-        Returns:
-            True if this property is the owl:topDataProperty.
-        """
-        return False
-
-
-class OWLRestriction(OWLAnonymousClassExpression):
-    """Represents an Object Property Restriction or Data Property Restriction in the OWL 2 specification."""
-    __slots__ = ()
-
-    @abstractmethod
-    def get_property(self) -> OWLPropertyExpression:
-        """
-        Returns:
-            Property being restricted.
-        """
-        pass
-
-    def is_data_restriction(self) -> bool:
-        """Determines if this is a data restriction.
-
-        Returns:
-            True if this is a data restriction.
-        """
-        return False
-
-    def is_object_restriction(self) -> bool:
-        """Determines if this is an object restriction.
-
-        Returns:
-            True if this is an object restriction.
-        """
-        return False
-
-
-class OWLObjectPropertyExpression(OWLPropertyExpression):
-    """A high level interface to describe different types of object properties."""
-    __slots__ = ()
-
-    @abstractmethod
-    def get_inverse_property(self) -> 'OWLObjectPropertyExpression':
-        """Obtains the property that corresponds to the inverse of this property.
-
-        Returns:
-            The inverse of this property. Note that this property will not necessarily be in the simplest form.
-        """
-        pass
-
-    @abstractmethod
-    def get_named_property(self) -> 'OWLObjectProperty':
-        """Get the named object property used in this property expression.
-
-        Returns:
-            P if this expression is either inv(P) or P.
-        """
-        pass
-
-    def is_object_property_expression(self) -> bool:
-        # documented in parent
-        return True
-
-
-class OWLDataPropertyExpression(OWLPropertyExpression, metaclass=ABCMeta):
-    """A high level interface to describe different types of data properties."""
-    __slots__ = ()
-
-    def is_data_property_expression(self):
-        # documented in parent
-        return True
-
-
-class OWLProperty(OWLPropertyExpression, OWLEntity, metaclass=ABCMeta):
-    """A marker interface for properties that aren't expression i.e. named properties. By definition, properties
-    are either data properties or object properties."""
-    __slots__ = ()
-    pass
-
-
-class OWLDataProperty(OWLDataPropertyExpression, OWLProperty):
-    """Represents a Data Property in the OWL 2 Specification."""
-    __slots__ = '_iri'
-    type_index: Final = 1004
-
-    _iri: IRI
-
-    def __init__(self, iri: IRI):
-        """Gets an instance of OWLDataProperty that has the specified IRI.
-
-        Args:
-            iri: The IRI.
-        """
-        self._iri = iri
-
-    def get_iri(self) -> IRI:
-        # documented in parent
-        return self._iri
-
-    def is_owl_top_data_property(self) -> bool:
-        # documented in parent
-        return self.get_iri() == OWLRDFVocabulary.OWL_TOP_DATA_PROPERTY.get_iri()
 
-
-class OWLObjectProperty(OWLObjectPropertyExpression, OWLProperty):
-    """Represents an Object Property in the OWL 2 Specification."""
+class OWLImportsDeclaration(HasIRI):
+    """Represents an import statement in an ontology."""
     __slots__ = '_iri'
-    type_index: Final = 1002
-
-    _iri: IRI
-
-    def get_named_property(self) -> 'OWLObjectProperty':
-        # documented in parent
-        return self
-
-    def __init__(self, iri: IRI):
-        """Gets an instance of OWLObjectProperty that has the specified IRI.
 
-        Args:
-            iri: The IRI.
+    def __init__(self, import_iri: IRI):
         """
-        self._iri = iri
-
-    def get_inverse_property(self) -> 'OWLObjectInverseOf':
-        # documented in parent
-        return OWLObjectInverseOf(self)
-
-    def get_iri(self) -> IRI:
-        # documented in parent
-        return self._iri
-
-    def is_owl_top_object_property(self) -> bool:
-        # documented in parent
-        return self.get_iri() == OWLRDFVocabulary.OWL_TOP_OBJECT_PROPERTY.get_iri()
-
-    @property
-    def str(self)->str:
-        return self.get_iri().as_str()
-
-
-class OWLObjectInverseOf(OWLObjectPropertyExpression):
-    """Represents the inverse of a property expression (ObjectInverseOf). This can be used to refer to the inverse of
-    a property, without actually naming the property. For example, consider the property hasPart, the inverse property
-    of hasPart (isPartOf) can be referred to using this interface inverseOf(hasPart), which can be used in
-    restrictions e.g. inverseOf(hasPart) some Car refers to the set of things that are part of at least one car."""
-    __slots__ = '_inverse_property'
-    type_index: Final = 1003
-
-    _inverse_property: OWLObjectProperty
-
-    def __init__(self, property: OWLObjectProperty):
-        """Gets the inverse of an object property.
-
         Args:
-            property: The property of which the inverse will be returned.
-        """
-        self._inverse_property = property
-
-    def get_inverse(self) -> OWLObjectProperty:
-        """Gets the property expression that this is the inverse of.
+            import_import_iri: Imported ontology.
 
         Returns:
-            The object property expression such that this object property expression is an inverse of it.
+            An imports declaration.
         """
-        return self._inverse_property
-
-    def get_inverse_property(self) -> OWLObjectProperty:
-        # documented in parent
-        return self.get_inverse()
-
-    def get_named_property(self) -> OWLObjectProperty:
-        # documented in parent
-        return self._inverse_property
-
-    def __repr__(self):
-        return f"OWLObjectInverseOf({repr(self._inverse_property)})"
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._inverse_property == other._inverse_property
-        return NotImplemented
-
-    def __hash__(self):
-        return hash(self._inverse_property)
-
-
-class OWLDataRestriction(OWLRestriction, metaclass=ABCMeta):
-    """Represents a Data Property Restriction in the OWL 2 specification."""
-    __slots__ = ()
-
-    def is_data_restriction(self) -> bool:
-        # documented in parent
-        return True
-
-    pass
-
-
-class OWLObjectRestriction(OWLRestriction, metaclass=ABCMeta):
-    """Represents a Object Property Restriction in the OWL 2 specification."""
-    __slots__ = ()
-
-    def is_object_restriction(self) -> bool:
-        # documented in parent
-        return True
-
-    @abstractmethod
-    def get_property(self) -> OWLObjectPropertyExpression:
-        # documented in parent
-        pass
-
-
-class HasFiller(Generic[_T], metaclass=ABCMeta):
-    """An interface to objects that have a filler.
-
-    Args:
-        _T: Filler type.
-    """
-    __slots__ = ()
+        self._iri = import_iri
 
-    @abstractmethod
-    def get_filler(self) -> _T:
-        """Gets the filler for this restriction. In the case of an object restriction this will be an individual, in
-        the case of a data restriction this will be a constant (data value). For quantified restriction this will be
-        a class expression or a data range.
+    def get_iri(self) -> IRI:
+        """Gets the import IRI.
 
         Returns:
-            the value
+            The import IRI that points to the ontology to be imported. The imported ontology might have this IRI as
+            its ontology IRI but this is not mandated. For example, an ontology with a non-resolvable ontology IRI
+            can be deployed at a resolvable URL.
         """
-        pass
-
-
-class OWLHasValueRestriction(Generic[_T], OWLRestriction, HasFiller[_T], metaclass=ABCMeta):
-    """OWLHasValueRestriction.
-
-    Args:
-        _T: The value type.
-    """
-    __slots__ = ()
-
-    _v: _T
-
-    def __init__(self, value: _T):
-        self._v = value
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._v == other._v
-        return NotImplemented
-
-    def __hash__(self):
-        return hash(self._v)
-
-    def get_filler(self) -> _T:
-        # documented in parent
-        return self._v
-
-
-class OWLQuantifiedRestriction(Generic[_T], OWLRestriction, HasFiller[_T], metaclass=ABCMeta):
-    """Represents a quantified restriction.
-
-    Args:
-        _T: value type
-    """
-    __slots__ = ()
-    pass
-
-
-class OWLQuantifiedObjectRestriction(OWLQuantifiedRestriction[OWLClassExpression], OWLObjectRestriction,
-                                     metaclass=ABCMeta):
-    """Represents a quantified object restriction."""
-    __slots__ = ()
-
-    _filler: OWLClassExpression
-
-    def __init__(self, filler: OWLClassExpression):
-        self._filler = filler
-
-    def get_filler(self) -> OWLClassExpression:
-        # documented in parent (HasFiller)
-        return self._filler
-
-
-class OWLObjectSomeValuesFrom(OWLQuantifiedObjectRestriction):
-    """Represents an ObjectSomeValuesFrom class expression in the OWL 2 Specification."""
-    __slots__ = '_property', '_filler'
-    type_index: Final = 3005
-
-    def __init__(self, property: OWLObjectPropertyExpression, filler: OWLClassExpression):
-        """Gets an OWLObjectSomeValuesFrom restriction.
-
-        Args:
-            property: The object property that the restriction acts along.
-            filler: The class expression that is the filler.
-
-        Returns:
-            An OWLObjectSomeValuesFrom restriction along the specified property with the specified filler.
-        """
-        super().__init__(filler)
-        self._property = property
-
-    def __repr__(self):
-        return f"OWLObjectSomeValuesFrom(property={repr(self._property)},filler={repr(self._filler)})"
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._filler == other._filler and self._property == other._property
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._filler, self._property))
-
-    def get_property(self) -> OWLObjectPropertyExpression:
-        # documented in parent
-        return self._property
-
-
-class OWLObjectAllValuesFrom(OWLQuantifiedObjectRestriction):
-    """Represents an ObjectAllValuesFrom class expression in the OWL 2 Specification."""
-    __slots__ = '_property', '_filler'
-    type_index: Final = 3006
-
-    def __init__(self, property: OWLObjectPropertyExpression, filler: OWLClassExpression):
-        super().__init__(filler)
-        self._property = property
-
-    def __repr__(self):
-        return f"OWLObjectAllValuesFrom(property={repr(self._property)},filler={repr(self._filler)})"
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._filler == other._filler and self._property == other._property
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._filler, self._property))
-
-    def get_property(self) -> OWLObjectPropertyExpression:
-        # documented in parent
-        return self._property
-
-
-class OWLNaryBooleanClassExpression(OWLBooleanClassExpression, HasOperands[OWLClassExpression]):
-    """OWLNaryBooleanClassExpression."""
-    __slots__ = ()
-
-    _operands: Sequence[OWLClassExpression]
-
-    def __init__(self, operands: Iterable[OWLClassExpression]):
-        """
-        Args:
-            operands: Class expressions.
-        """
-        self._operands = tuple(operands)
-
-    def operands(self) -> Iterable[OWLClassExpression]:
-        # documented in parent
-        yield from self._operands
-
-    def __repr__(self):
-        return f'{type(self).__name__}({repr(self._operands)})'
-
-    def __eq__(self, other):
-        if type(other) == type(self):
-            return self._operands == other._operands
-        return NotImplemented
-
-    def __hash__(self):
-        return hash(self._operands)
-
-
-class OWLObjectUnionOf(OWLNaryBooleanClassExpression):
-    """Represents an ObjectUnionOf class expression in the OWL 2 Specification."""
-    __slots__ = '_operands'
-    type_index: Final = 3002
-
-    _operands: Sequence[OWLClassExpression]
-
-
-class OWLObjectIntersectionOf(OWLNaryBooleanClassExpression):
-    """Represents an OWLObjectIntersectionOf class expression in the OWL 2 Specification."""
-    __slots__ = '_operands'
-    type_index: Final = 3001
-
-    _operands: Sequence[OWLClassExpression]
-
-
-class HasCardinality(metaclass=ABCMeta):
-    """An interface to objects that have a cardinality."""
-    __slots__ = ()
-
-    @abstractmethod
-    def get_cardinality(self) -> int:
-        """Gets the cardinality of a restriction.
-
-        Returns:
-            The cardinality. A non-negative integer.
-        """
-        pass
-
-
-_F = TypeVar('_F', bound=OWLPropertyRange)  #:
-
-
-class OWLCardinalityRestriction(Generic[_F], OWLQuantifiedRestriction[_F], HasCardinality, metaclass=ABCMeta):
-    """Base interface for owl min and max cardinality restriction.
-
-    Args:
-        _F: Type of filler.
-    """
-    __slots__ = ()
-
-    _cardinality: int
-    _filler: _F
-
-    def __init__(self, cardinality: int, filler: _F):
-        self._cardinality = cardinality
-        self._filler = filler
-
-    def get_cardinality(self) -> int:
-        # documented in parent
-        return self._cardinality
-
-    def get_filler(self) -> _F:
-        # documented in parent
-        return self._filler
-
-
-class OWLObjectCardinalityRestriction(OWLCardinalityRestriction[OWLClassExpression], OWLQuantifiedObjectRestriction):
-    """Represents Object Property Cardinality Restrictions in the OWL 2 specification."""
-    __slots__ = ()
-
-    _property: OWLObjectPropertyExpression
-
-    @abstractmethod
-    def __init__(self, cardinality: int, property: OWLObjectPropertyExpression, filler: OWLClassExpression):
-        super().__init__(cardinality, filler)
-        self._property = property
-
-    def get_property(self) -> OWLObjectPropertyExpression:
-        # documented in parent
-        return self._property
-
-    def __repr__(self):
-        return f"{type(self).__name__}(" \
-               f"property={repr(self.get_property())},{self.get_cardinality()},filler={repr(self.get_filler())})"
-
-    def __eq__(self, other):
-        if type(other) == type(self):
-            return self._property == other._property \
-                   and self._cardinality == other._cardinality \
-                   and self._filler == other._filler
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._property, self._cardinality, self._filler))
-
-
-class OWLObjectMinCardinality(OWLObjectCardinalityRestriction):
-    """Represents a ObjectMinCardinality restriction in the OWL 2 Specification."""
-    __slots__ = '_cardinality', '_filler', '_property'
-    type_index: Final = 3008
-
-    def __init__(self, cardinality: int, property: OWLObjectPropertyExpression, filler: OWLClassExpression):
-        """
-        Args:
-            cardinality: Cannot be negative.
-            property: The property that the restriction acts along.
-            filler: Class expression for restriction.
-
-        Returns:
-            An ObjectMinCardinality on the specified property.
-        """
-        super().__init__(cardinality, property, filler)
-
-
-class OWLObjectMaxCardinality(OWLObjectCardinalityRestriction):
-    """Represents a ObjectMaxCardinality restriction in the OWL 2 Specification."""
-    __slots__ = '_cardinality', '_filler', '_property'
-    type_index: Final = 3010
-
-    def __init__(self, cardinality: int, property: OWLObjectPropertyExpression, filler: OWLClassExpression):
-        """
-        Args:
-            cardinality: Cannot be negative.
-            property: The property that the restriction acts along.
-            filler: Class expression for restriction.
-
-        Returns:
-            An ObjectMaxCardinality on the specified property.
-        """
-        super().__init__(cardinality, property, filler)
-
-
-class OWLObjectExactCardinality(OWLObjectCardinalityRestriction):
-    """Represents an ObjectExactCardinality  restriction in the OWL 2 Specification."""
-    __slots__ = '_cardinality', '_filler', '_property'
-    type_index: Final = 3009
-
-    def __init__(self, cardinality: int, property: OWLObjectPropertyExpression, filler: OWLClassExpression):
-        """
-        Args:
-            cardinality: Cannot be negative.
-            property: The property that the restriction acts along.
-            filler: Class expression for restriction.
-
-        Returns:
-            An ObjectExactCardinality on the specified property.
-        """
-        super().__init__(cardinality, property, filler)
-
-    def as_intersection_of_min_max(self) -> OWLObjectIntersectionOf:
-        """Obtains an equivalent form that is a conjunction of a min cardinality and max cardinality restriction.
-
-        Returns:
-            The semantically equivalent but structurally simpler form (= 1 R C) = >= 1 R C and <= 1 R C.
-        """
-        args = self.get_cardinality(), self.get_property(), self.get_filler()
-        return OWLObjectIntersectionOf((OWLObjectMinCardinality(*args), OWLObjectMaxCardinality(*args)))
-
-
-class OWLObjectHasSelf(OWLObjectRestriction):
-    """Represents an ObjectHasSelf class expression in the OWL 2 Specification."""
-    __slots__ = '_property'
-    type_index: Final = 3011
-
-    _property: OWLObjectPropertyExpression
-
-    def __init__(self, property: OWLObjectPropertyExpression):
-        """Object has self restriction
-
-        Args:
-            property: The property that the restriction acts along.
-
-        Returns:
-            A ObjectHasSelf class expression on the specified property.
-        """
-        self._property = property
-
-    def get_property(self) -> OWLObjectPropertyExpression:
-        # documented in parent
-        return self._property
-
-    def __eq__(self, other):
-        if type(other) == type(self):
-            return self._property == other._property
-        return NotImplemented
-
-    def __hash__(self):
-        return hash(self._property)
-
-    def __repr__(self):
-        return f'OWLObjectHasSelf({self._property})'
-
-
-class OWLIndividual(OWLObject, metaclass=ABCMeta):
-    """Represents a named or anonymous individual."""
-    __slots__ = ()
-    pass
-
-
-class OWLObjectHasValue(OWLHasValueRestriction[OWLIndividual], OWLObjectRestriction):
-    """Represents an ObjectHasValue class expression in the OWL 2 Specification."""
-    __slots__ = '_property', '_v'
-    type_index: Final = 3007
-
-    _property: OWLObjectPropertyExpression
-    _v: OWLIndividual
-
-    def __init__(self, property: OWLObjectPropertyExpression, individual: OWLIndividual):
-        """
-        Args:
-            property: The property that the restriction acts along.
-            individual: Individual for restriction.
-
-        Returns:
-            A HasValue restriction with specified property and value
-        """
-        super().__init__(individual)
-        self._property = property
-
-    def get_property(self) -> OWLObjectPropertyExpression:
-        # documented in parent
-        return self._property
-
-    def as_some_values_from(self) -> OWLClassExpression:
-        """A convenience method that obtains this restriction as an existential restriction with a nominal filler.
-
-        Returns:
-            The existential equivalent of this value restriction. simp(HasValue(p a)) = some(p {a}).
-        """
-        return OWLObjectSomeValuesFrom(self.get_property(), OWLObjectOneOf(self.get_filler()))
-
-    def __repr__(self):
-        return f'OWLObjectHasValue(property={self.get_property()}, individual={self._v})'
-
-
-class OWLObjectOneOf(OWLAnonymousClassExpression, HasOperands[OWLIndividual]):
-    """Represents an ObjectOneOf class expression in the OWL 2 Specification."""
-    __slots__ = '_values'
-    type_index: Final = 3004
-
-    def __init__(self, values: Union[OWLIndividual, Iterable[OWLIndividual]]):
-        if isinstance(values, OWLIndividual):
-            self._values = values,
-        else:
-            for _ in values:
-                assert isinstance(_, OWLIndividual)
-            self._values = tuple(values)
-
-    def individuals(self) -> Iterable[OWLIndividual]:
-        """Gets the individuals that are in the oneOf. These individuals represent the exact instances (extension)
-         of this class expression.
-
-         Returns:
-             The individuals that are the values of this {@code ObjectOneOf} class expression.
-        """
-        yield from self._values
-
-    def operands(self) -> Iterable[OWLIndividual]:
-        # documented in parent
-        yield from self.individuals()
-
-    def as_object_union_of(self) -> OWLClassExpression:
-        """Simplifies this enumeration to a union of singleton nominals.
-
-        Returns:
-            This enumeration in a more standard DL form.
-            simp({a}) = {a} simp({a0, ... , {an}) = unionOf({a0}, ... , {an})
-        """
-        if len(self._values) == 1:
-            return self
-        return OWLObjectUnionOf(map(lambda _: OWLObjectOneOf(_), self.individuals()))
-
-    def __hash__(self):
-        return hash(self._values)
-
-    def __eq__(self, other):
-        if type(other) == type(self):
-            return self._values == other._values
-        return NotImplemented
-
-    def __repr__(self):
-        return f'OWLObjectOneOf({self._values})'
-
-
-class OWLNamedIndividual(OWLIndividual, OWLEntity):
-    """Represents a Named Individual in the OWL 2 Specification."""
-    __slots__ = '_iri'
-    type_index: Final = 1005
-
-    _iri: IRI
-
-    def __init__(self, iri: IRI):
-        """Gets an instance of OWLNamedIndividual that has the specified IRI.
-
-        Args:
-            iri: The IRI.
-
-        Returns:
-            An OWLNamedIndividual that has the specified IRI.
-        """
-        self._iri = iri
-
-    def get_iri(self) -> IRI:
-        # documented in parent
-        return self._iri
-
-    @property
-    def iri(self):
-        return self._iri
-
-    @property
-    def str(self):
-        return self._iri.as_str()
-
-
-_M = TypeVar('_M', bound='OWLOntologyManager')  #:
-
-
-class OWLOntologyID:
-    """An object that identifies an ontology. Since OWL 2, ontologies do not have to have an ontology IRI, or if they
-    have an ontology IRI then they can optionally also have a version IRI. Instances of this OWLOntologyID class bundle
-    identifying information of an ontology together. If an ontology doesn't have an ontology IRI then we say that it is
-    "anonymous".
-    """
-    __slots__ = '_ontology_iri', '_version_iri'
-
-    _ontology_iri: Optional[IRI]
-    _version_iri: Optional[IRI]
-
-    def __init__(self, ontology_iri: Optional[IRI] = None, version_iri: Optional[IRI] = None):
-        """Constructs an ontology identifier specifying the ontology IRI and version IRI.
-
-        Args:
-            ontology_iri: The ontology IRI (optional).
-            version_iri: The version IRI (must be None if no ontology_iri is provided).
-        """
-        self._ontology_iri = ontology_iri
-        self._version_iri = version_iri
-
-    def get_ontology_iri(self) -> Optional[IRI]:
-        """Gets the ontology IRI.
-
-        Returns:
-            Ontology IRI. If the ontology is anonymous, it will return None.
-        """
-        return self._ontology_iri
-
-    def get_version_iri(self) -> Optional[IRI]:
-        """Gets the version IRI.
-
-        Returns:
-            Version IRI or None.
-        """
-        return self._version_iri
-
-    def get_default_document_iri(self) -> Optional[IRI]:
-        """Gets the IRI which is used as a default for the document that contain a representation of an ontology with
-        this ID. This will be the version IRI if there is an ontology IRI and version IRI, else it will be the ontology
-        IRI if there is an ontology IRI but no version IRI, else it will be None if there is no ontology IRI. See
-        Ontology Documents in the OWL 2 Structural Specification.
-
-        Returns:
-            the IRI that can be used as a default for an ontology document, or None.
-        """
-        if self._ontology_iri is not None:
-            if self._version_iri is not None:
-                return self._version_iri
-        return self._ontology_iri
-
-    def is_anonymous(self) -> bool:
-        return self._ontology_iri is None
-
-    def __repr__(self):
-        return f"OWLOntologyID({repr(self._ontology_iri)}, {repr(self._version_iri)})"
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._ontology_iri == other._ontology_iri and self._version_iri == other._version_iri
-        return NotImplemented
-
-
-class OWLAxiom(OWLObject, metaclass=ABCMeta):
-    """Represents Axioms in the OWL 2 Specification.
-
-    An OWL ontology contains a set of axioms. These axioms can be annotation axioms, declaration axioms, imports axioms
-    or logical axioms.
-    """
-    __slots__ = '_annotations'
-
-    _annotations: List['OWLAnnotation']
-
-    def __init__(self, annotations: Optional[Iterable['OWLAnnotation']] = None):
-        self._annotations = list(annotations) if annotations is not None else list()
-
-    def annotations(self) -> Optional[List['OWLAnnotation']]:
-        return self._annotations
-
-    def is_annotated(self) -> bool:
-        return self._annotations is not None and len(self._annotations) > 0
-
-    def is_logical_axiom(self) -> bool:
-        return False
-
-    def is_annotation_axiom(self) -> bool:
-        return False
-    # TODO: XXX
-
-
-class OWLDatatype(OWLEntity, OWLDataRange):
-    """Represents a Datatype (named data range) in the OWL 2 Specification."""
-    __slots__ = '_iri'
-
-    type_index: Final = 4001
-
-    _iri: IRI
-
-    def __init__(self, iri: Union[IRI, HasIRI]):
-        """Gets an instance of OWLDatatype that has the specified IRI.
-
-        Args:
-            iri: The IRI.
-        """
-        if isinstance(iri, HasIRI):
-            self._iri = iri.get_iri()
-        else:
-            assert isinstance(iri, IRI)
-            self._iri = iri
-
-    def get_iri(self) -> 'IRI':
-        # documented in parent
-        return self._iri
-
-
-class OWLDatatypeRestriction(OWLDataRange):
-    """Represents a DatatypeRestriction data range in the OWL 2 Specification."""
-    __slots__ = '_type', '_facet_restrictions'
-
-    type_index: Final = 4006
-
-    _type: OWLDatatype
-    _facet_restrictions: Sequence['OWLFacetRestriction']
-
-    def __init__(self, type_: OWLDatatype, facet_restrictions: Union['OWLFacetRestriction',
-                                                                     Iterable['OWLFacetRestriction']]):
-        self._type = type_
-        if isinstance(facet_restrictions, OWLFacetRestriction):
-            facet_restrictions = facet_restrictions,
-        self._facet_restrictions = tuple(facet_restrictions)
-
-    def get_datatype(self) -> OWLDatatype:
-        return self._type
-
-    def get_facet_restrictions(self) -> Sequence['OWLFacetRestriction']:
-        return self._facet_restrictions
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._type == other._type \
-                   and self._facet_restrictions == other._facet_restrictions
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._type, self._facet_restrictions))
-
-    def __repr__(self):
-        return f'OWLDatatypeRestriction({repr(self._type)}, {repr(self._facet_restrictions)})'
-
-
-class OWLFacetRestriction(OWLObject):
-    """A facet restriction is used to restrict a particular datatype."""
-
-    __slots__ = '_facet', '_literal'
-
-    type_index: Final = 4007
-
-    _facet: OWLFacet
-    _literal: 'OWLLiteral'
-
-    def __init__(self, facet: OWLFacet, literal: Literals):
-        self._facet = facet
-        if isinstance(literal, OWLLiteral):
-            self._literal = literal
-        else:
-            self._literal = OWLLiteral(literal)
-
-    def get_facet(self) -> OWLFacet:
-        return self._facet
-
-    def get_facet_value(self) -> 'OWLLiteral':
-        return self._literal
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._facet == other._facet and self._literal == other._literal
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._facet, self._literal))
-
-    def __repr__(self):
-        return f'OWLFacetRestriction({self._facet}, {repr(self._literal)})'
-
-
-class OWLLiteral(OWLAnnotationValue, metaclass=ABCMeta):
-    """Represents a Literal in the OWL 2 Specification."""
-    __slots__ = ()
-
-    type_index: Final = 4008
-
-    def __new__(cls, value, type_: Optional[OWLDatatype] = None):
-        """Convenience method that obtains a literal.
-
-        Args:
-            value: The value of the literal.
-            type_: The datatype of the literal.
-        """
-        if type_ is not None:
-            if type_ == BooleanOWLDatatype:
-                return super().__new__(_OWLLiteralImplBoolean)
-            elif type_ == IntegerOWLDatatype:
-                return super().__new__(_OWLLiteralImplInteger)
-            elif type_ == DoubleOWLDatatype:
-                return super().__new__(_OWLLiteralImplDouble)
-            elif type_ == StringOWLDatatype:
-                return super().__new__(_OWLLiteralImplString)
-            elif type_ == DateOWLDatatype:
-                return super().__new__(_OWLLiteralImplDate)
-            elif type_ == DateTimeOWLDatatype:
-                return super().__new__(_OWLLiteralImplDateTime)
-            elif type_ == DurationOWLDatatype:
-                return super().__new__(_OWLLiteralImplDuration)
-            else:
-                return super().__new__(_OWLLiteralImpl)
-        if isinstance(value, bool):
-            return super().__new__(_OWLLiteralImplBoolean)
-        elif isinstance(value, int):
-            return super().__new__(_OWLLiteralImplInteger)
-        elif isinstance(value, float):
-            return super().__new__(_OWLLiteralImplDouble)
-        elif isinstance(value, str):
-            return super().__new__(_OWLLiteralImplString)
-        elif isinstance(value, datetime):
-            return super().__new__(_OWLLiteralImplDateTime)
-        elif isinstance(value, date):
-            return super().__new__(_OWLLiteralImplDate)
-        elif isinstance(value, Timedelta):
-            return super().__new__(_OWLLiteralImplDuration)
-        # TODO XXX
-        raise NotImplementedError(value)
-
-    def get_literal(self) -> str:
-        """Gets the lexical value of this literal. Note that the language tag is not included.
-
-        Returns:
-            The lexical value of this literal.
-        """
-        return str(self._v)
-
-    def is_boolean(self) -> bool:
-        """Whether this literal is typed as boolean."""
-        return False
-
-    def parse_boolean(self) -> bool:
-        """Parses the lexical value of this literal into a bool. The lexical value of this literal should be in the
-        lexical space of the boolean datatype ("http://www.w3.org/2001/XMLSchema#boolean").
-
-        Returns:
-            A bool value that is represented by this literal.
-        """
-        raise ValueError
-
-    def is_double(self) -> bool:
-        """Whether this literal is typed as double."""
-        return False
-
-    def parse_double(self) -> float:
-        """Parses the lexical value of this literal into a double. The lexical value of this literal should be in the
-        lexical space of the double datatype ("http://www.w3.org/2001/XMLSchema#double").
-
-        Returns:
-            A double value that is represented by this literal.
-        """
-        raise ValueError
-
-    def is_integer(self) -> bool:
-        """Whether this literal is typed as integer."""
-        return False
-
-    def parse_integer(self) -> int:
-        """Parses the lexical value of this literal into an integer. The lexical value of this literal should be in the
-        lexical space of the integer datatype ("http://www.w3.org/2001/XMLSchema#integer").
-
-        Returns:
-            An integer value that is represented by this literal.
-        """
-        raise ValueError
-
-    def is_string(self) -> bool:
-        """Whether this literal is typed as string."""
-        return False
-
-    def parse_string(self) -> str:
-        """Parses the lexical value of this literal into a string. The lexical value of this literal should be in the
-        lexical space of the string datatype ("http://www.w3.org/2001/XMLSchema#string").
-
-        Returns:
-            A string value that is represented by this literal.
-        """
-        raise ValueError
-
-    def is_date(self) -> bool:
-        """Whether this literal is typed as date."""
-        return False
-
-    def parse_date(self) -> date:
-        """Parses the lexical value of this literal into a date. The lexical value of this literal should be in the
-        lexical space of the date datatype ("http://www.w3.org/2001/XMLSchema#date").
-
-        Returns:
-            A date value that is represented by this literal.
-        """
-        raise ValueError
-
-    def is_datetime(self) -> bool:
-        """Whether this literal is typed as dateTime."""
-        return False
-
-    def parse_datetime(self) -> datetime:
-        """Parses the lexical value of this literal into a datetime. The lexical value of this literal should be in the
-        lexical space of the dateTime datatype ("http://www.w3.org/2001/XMLSchema#dateTime").
-
-        Returns:
-            A datetime value that is represented by this literal.
-        """
-        raise ValueError
-
-    def is_duration(self) -> bool:
-        """Whether this literal is typed as duration."""
-        return False
-
-    def parse_duration(self) -> Timedelta:
-        """Parses the lexical value of this literal into a Timedelta. The lexical value of this literal should be in the
-        lexical space of the duration datatype ("http://www.w3.org/2001/XMLSchema#duration").
-
-        Returns:
-            A Timedelta value that is represented by this literal.
-        """
-        raise ValueError
-
-    # noinspection PyMethodMayBeStatic
-    def is_literal(self) -> bool:
-        # documented in parent
-        return True
-
-    def as_literal(self) -> 'OWLLiteral':
-        # documented in parent
-        return self
-
-    def to_python(self) -> Literals:
-        return self._v
-
-    @abstractmethod
-    def get_datatype(self) -> OWLDatatype:
-        """Gets the OWLDatatype which types this literal.
-
-        Returns:
-            The OWLDatatype that types this literal.
-        """
-        pass
-
-
-@total_ordering
-class _OWLLiteralImplDouble(OWLLiteral):
-    __slots__ = '_v'
-
-    _v: float
-
-    def __init__(self, value, type_=None):
-        assert type_ is None or type_ == DoubleOWLDatatype
-        if not isinstance(value, float):
-            value = float(value)
-        self._v = value
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._v == other._v
-        return NotImplemented
-
-    def __lt__(self, other):
-        if type(other) is type(self):
-            return self._v < other._v
-        return NotImplemented
-
-    def __hash__(self):
-        return hash(self._v)
-
-    def __repr__(self):
-        return f'OWLLiteral({self._v})'
-
-    def is_double(self) -> bool:
-        return True
-
-    def parse_double(self) -> float:
-        # documented in parent
-        return self._v
-
-    # noinspection PyMethodMayBeStatic
-    def get_datatype(self) -> OWLDatatype:
-        # documented in parent
-        return DoubleOWLDatatype
-
-
-@total_ordering
-class _OWLLiteralImplInteger(OWLLiteral):
-    __slots__ = '_v'
-
-    _v: int
-
-    def __init__(self, value, type_=None):
-        assert type_ is None or type_ == IntegerOWLDatatype
-        if not isinstance(value, int):
-            value = int(value)
-        self._v = value
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._v == other._v
-        return NotImplemented
-
-    def __lt__(self, other):
-        if type(other) is type(self):
-            return self._v < other._v
-        return NotImplemented
-
-    def __hash__(self):
-        return hash(self._v)
-
-    def __repr__(self):
-        return f'OWLLiteral({self._v})'
-
-    def is_integer(self) -> bool:
-        return True
-
-    def parse_integer(self) -> int:
-        # documented in parent
-        return self._v
-
-    # noinspection PyMethodMayBeStatic
-    def get_datatype(self) -> OWLDatatype:
-        # documented in parent
-        return IntegerOWLDatatype
-
-
-class _OWLLiteralImplBoolean(OWLLiteral):
-    __slots__ = '_v'
-
-    _v: bool
-
-    def __init__(self, value, type_=None):
-        assert type_ is None or type_ == BooleanOWLDatatype
-        if not isinstance(value, bool):
-            from distutils.util import strtobool
-            value = bool(strtobool(value))
-        self._v = value
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._v == other._v
-        return NotImplemented
-
-    def __hash__(self):
-        return hash(self._v)
-
-    def __repr__(self):
-        return f'OWLLiteral({self._v})'
-
-    def is_boolean(self) -> bool:
-        return True
-
-    def parse_boolean(self) -> bool:
-        # documented in parent
-        return self._v
-
-    # noinspection PyMethodMayBeStatic
-    def get_datatype(self) -> OWLDatatype:
-        # documented in parent
-        return BooleanOWLDatatype
-
-
-@total_ordering
-class _OWLLiteralImplString(OWLLiteral):
-    __slots__ = '_v'
-
-    _v: str
-
-    def __init__(self, value, type_=None):
-        assert type_ is None or type_ == StringOWLDatatype
-        if not isinstance(value, str):
-            value = str(value)
-        self._v = value
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._v == other._v
-        return NotImplemented
-
-    def __lt__(self, other):
-        if type(other) is type(self):
-            return self._v < other._v
-        return NotImplemented
-
-    def __len__(self):
-        return len(self._v)
-
-    def __hash__(self):
-        return hash(self._v)
-
-    def __repr__(self):
-        return f'OWLLiteral({self._v})'
-
-    def is_string(self) -> bool:
-        return True
-
-    def parse_string(self) -> str:
-        # documented in parent
-        return self._v
-
-    # noinspection PyMethodMayBeStatic
-    def get_datatype(self) -> OWLDatatype:
-        # documented in parent
-        return StringOWLDatatype
-
-
-@total_ordering
-class _OWLLiteralImplDate(OWLLiteral):
-    __slots__ = '_v'
-
-    _v: date
-
-    def __init__(self, value, type_=None):
-        assert type_ is None or type_ == DateOWLDatatype
-        if not isinstance(value, date):
-            value = date.fromisoformat(value)
-        self._v = value
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._v == other._v
-        return NotImplemented
-
-    def __lt__(self, other):
-        if type(other) is type(self):
-            return self._v < other._v
-        return NotImplemented
-
-    def __hash__(self):
-        return hash(self._v)
-
-    def __repr__(self):
-        return f'OWLLiteral({self._v})'
-
-    def is_date(self) -> bool:
-        return True
-
-    def parse_date(self) -> date:
-        # documented in parent
-        return self._v
-
-    # noinspection PyMethodMayBeStatic
-    def get_datatype(self) -> OWLDatatype:
-        # documented in parent
-        return DateOWLDatatype
-
-
-@total_ordering
-class _OWLLiteralImplDateTime(OWLLiteral):
-    __slots__ = '_v'
-
-    _v: datetime
-
-    def __init__(self, value, type_=None):
-        assert type_ is None or type_ == DateTimeOWLDatatype
-        if not isinstance(value, datetime):
-            value = value.replace("Z", "+00:00") if isinstance(value, str) and value[-1] == "Z" else value
-            value = datetime.fromisoformat(value)
-        self._v = value
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._v == other._v
-        return NotImplemented
-
-    def __lt__(self, other):
-        if type(other) is type(self):
-            return self._v < other._v
-        return NotImplemented
-
-    def __hash__(self):
-        return hash(self._v)
-
-    def __repr__(self):
-        return f'OWLLiteral({self._v})'
-
-    def is_datetime(self) -> bool:
-        return True
-
-    def parse_datetime(self) -> datetime:
-        # documented in parent
-        return self._v
-
-    # noinspection PyMethodMayBeStatic
-    def get_datatype(self) -> OWLDatatype:
-        # documented in parent
-        return DateTimeOWLDatatype
-
-
-@total_ordering
-class _OWLLiteralImplDuration(OWLLiteral):
-    __slots__ = '_v'
-
-    _v: Timedelta
-
-    def __init__(self, value, type_=None):
-        assert type_ is None or type_ == DurationOWLDatatype
-        if not isinstance(value, Timedelta):
-            value = Timedelta(value)
-        self._v = value
-
-    def get_literal(self) -> str:
-        return self._v.isoformat()
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._v == other._v
-        return NotImplemented
-
-    def __lt__(self, other):
-        if type(other) is type(self):
-            return self._v < other._v
-        return NotImplemented
-
-    def __hash__(self):
-        return hash(self._v)
-
-    def __repr__(self):
-        return f'OWLLiteral({self._v})'
-
-    def is_duration(self) -> bool:
-        return True
-
-    def parse_duration(self) -> Timedelta:
-        # documented in parent
-        return self._v
-
-    # noinspection PyMethodMayBeStatic
-    def get_datatype(self) -> OWLDatatype:
-        # documented in parent
-        return DurationOWLDatatype
-
-
-class _OWLLiteralImpl(OWLLiteral):
-    __slots__ = '_v', '_datatype'
-
-    def __init__(self, v, type_: OWLDatatype):
-        assert isinstance(type_, OWLDatatype)
-        self._v = v
-        self._datatype = type_
-
-    def get_datatype(self) -> OWLDatatype:
-        return self._datatype
-
-    def __eq__(self, other):
-        if type(other) is type(self) and other.get_datatype() == self.get_datatype():
-            return self._v == other._v
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._v, self._datatype))
-
-    def __repr__(self):
-        return f'OWLLiteral({repr(self._v)}, {self._datatype})'
-
-
-class OWLQuantifiedDataRestriction(OWLQuantifiedRestriction[OWLDataRange],
-                                   OWLDataRestriction, metaclass=ABCMeta):
-    """Represents a quantified data restriction."""
-    __slots__ = ()
-
-    _filler: OWLDataRange
-
-    def __init__(self, filler: OWLDataRange):
-        self._filler = filler
-
-    def get_filler(self) -> OWLDataRange:
-        # documented in parent (HasFiller)
-        return self._filler
-
-
-class OWLDataCardinalityRestriction(OWLCardinalityRestriction[OWLDataRange],
-                                    OWLQuantifiedDataRestriction,
-                                    OWLDataRestriction, metaclass=ABCMeta):
-    """Represents Data Property Cardinality Restrictions in the OWL 2 specification."""
-    __slots__ = ()
-
-    _property: OWLDataPropertyExpression
-
-    @abstractmethod
-    def __init__(self, cardinality: int, property: OWLDataPropertyExpression, filler: OWLDataRange):
-        super().__init__(cardinality, filler)
-        self._property = property
-
-    def get_property(self) -> OWLDataPropertyExpression:
-        # documented in parent
-        return self._property
-
-    def __repr__(self):
-        return f"{type(self).__name__}(" \
-               f"property={repr(self.get_property())},{self.get_cardinality()},filler={repr(self.get_filler())})"
-
-    def __eq__(self, other):
-        if type(other) == type(self):
-            return self._property == other._property \
-                   and self._cardinality == other._cardinality \
-                   and self._filler == other._filler
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._property, self._cardinality, self._filler))
-
-
-class OWLDataAllValuesFrom(OWLQuantifiedDataRestriction):
-    """Represents DataAllValuesFrom class expressions in the OWL 2 Specification."""
-    __slots__ = '_property'
-
-    type_index: Final = 3013
-
-    _property: OWLDataPropertyExpression
-
-    def __init__(self, property: OWLDataPropertyExpression, filler: OWLDataRange):
-        """Gets an OWLDataAllValuesFrom restriction.
-
-        Args:
-            property: The data property that the restriction acts along.
-            filler: The data range that is the filler.
-
-        Returns:
-            An OWLDataAllValuesFrom restriction along the specified property with the specified filler.
-        """
-        super().__init__(filler)
-        self._property = property
-
-    def __repr__(self):
-        return f"OWLDataAllValuesFrom(property={repr(self._property)},filler={repr(self._filler)})"
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._filler == other._filler and self._property == other._property
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._filler, self._property))
-
-    def get_property(self) -> OWLDataPropertyExpression:
-        # documented in parent
-        return self._property
-
-
-class OWLDataComplementOf(OWLDataRange):
-    """Represents DataComplementOf in the OWL 2 Specification."""
-    type_index: Final = 4002
-
-    _data_range: OWLDataRange
-
-    def __init__(self, data_range: OWLDataRange):
-        """
-        Args:
-            data_range: Data range to complement.
-        """
-        self._data_range = data_range
-
-    def get_data_range(self) -> OWLDataRange:
-        """
-        Returns:
-            The wrapped data range.
-        """
-        return self._data_range
-
-    def __repr__(self):
-        return f"OWLDataComplementOf({repr(self._data_range)})"
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._data_range == other._data_range
-        return NotImplemented
-
-    def __hash__(self):
-        return hash(self._data_range)
-
-
-class OWLDataExactCardinality(OWLDataCardinalityRestriction):
-    """Represents DataExactCardinality restrictions in the OWL 2 Specification."""
-    __slots__ = '_cardinality', '_filler', '_property'
-
-    type_index: Final = 3016
-
-    def __init__(self, cardinality: int, property: OWLDataPropertyExpression, filler: OWLDataRange):
-        """
-        Args:
-            cardinality: Cannot be negative.
-            property: The property that the restriction acts along.
-            filler: Data range for restriction
-
-        Returns:
-            A DataExactCardinality on the specified property.
-        """
-        super().__init__(cardinality, property, filler)
-
-    def as_intersection_of_min_max(self) -> OWLObjectIntersectionOf:
-        """Obtains an equivalent form that is a conjunction of a min cardinality and max cardinality restriction.
-
-        Returns:
-            The semantically equivalent but structurally simpler form (= 1 R D) = >= 1 R D and <= 1 R D.
-        """
-        args = self.get_cardinality(), self.get_property(), self.get_filler()
-        return OWLObjectIntersectionOf((OWLDataMinCardinality(*args), OWLDataMaxCardinality(*args)))
-
-
-class OWLDataHasValue(OWLHasValueRestriction[OWLLiteral], OWLDataRestriction):
-    """Represents DataHasValue restrictions in the OWL 2 Specification."""
-    __slots__ = '_property'
-
-    type_index: Final = 3014
-
-    _property: OWLDataPropertyExpression
-
-    def __init__(self, property: OWLDataPropertyExpression, value: OWLLiteral):
-        """Gets an OWLDataHasValue restriction.
-
-        Args:
-            property: The data property that the restriction acts along.
-            filler: The literal value.
-
-        Returns:
-            An OWLDataHasValue restriction along the specified property with the specified literal.
-        """
-        super().__init__(value)
-        self._property = property
-
-    def __repr__(self):
-        return f"OWLDataHasValue(property={repr(self._property)},value={repr(self._v)})"
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._v == other._v and self._property == other._property
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._v, self._property))
-
-    def as_some_values_from(self) -> OWLClassExpression:
-        """A convenience method that obtains this restriction as an existential restriction with a nominal filler.
-
-        Returns:
-            The existential equivalent of this value restriction. simp(HasValue(p a)) = some(p {a}).
-        """
-        return OWLDataSomeValuesFrom(self.get_property(), OWLDataOneOf(self.get_filler()))
-
-    def get_property(self) -> OWLDataPropertyExpression:
-        # documented in parent
-        return self._property
-
-
-class OWLDataMaxCardinality(OWLDataCardinalityRestriction):
-    """Represents DataMaxCardinality restrictions in the OWL 2 Specification."""
-    __slots__ = '_cardinality', '_filler', '_property'
-
-    type_index: Final = 3017
-
-    def __init__(self, cardinality: int, property: OWLDataPropertyExpression, filler: OWLDataRange):
-        """
-        Args:
-            cardinality: Cannot be negative.
-            property: The property that the restriction acts along.
-            filler: Data range for restriction.
-
-        Returns:
-            A DataMaxCardinality on the specified property.
-        """
-        super().__init__(cardinality, property, filler)
-
-
-class OWLDataMinCardinality(OWLDataCardinalityRestriction):
-    """Represents DataMinCardinality restrictions in the OWL 2 Specification."""
-    __slots__ = '_cardinality', '_filler', '_property'
-
-    type_index: Final = 3015
-
-    def __init__(self, cardinality: int, property: OWLDataPropertyExpression, filler: OWLDataRange):
-        """
-        Args:
-            cardinality: Cannot be negative.
-            property: The property that the restriction acts along.
-            filler: Data range for restriction.
-
-        Returns:
-            A DataMinCardinality on the specified property.
-        """
-        super().__init__(cardinality, property, filler)
-
-
-class OWLDataOneOf(OWLDataRange, HasOperands[OWLLiteral]):
-    """Represents DataOneOf in the OWL 2 Specification."""
-    type_index: Final = 4003
-
-    _values: Sequence[OWLLiteral]
-
-    def __init__(self, values: Union[OWLLiteral, Iterable[OWLLiteral]]):
-        if isinstance(values, OWLLiteral):
-            self._values = values,
-        else:
-            for _ in values:
-                assert isinstance(_, OWLLiteral)
-            self._values = tuple(values)
-
-    def values(self) -> Iterable[OWLLiteral]:
-        """Gets the values that are in the oneOf.
-
-         Returns:
-             The values of this {@code DataOneOf} class expression.
-        """
-        yield from self._values
-
-    def operands(self) -> Iterable[OWLLiteral]:
-        # documented in parent
-        yield from self.values()
-
-    def __hash__(self):
-        return hash(self._values)
-
-    def __eq__(self, other):
-        if type(other) == type(self):
-            return self._values == other._values
-        return NotImplemented
-
-    def __repr__(self):
-        return f'OWLDataOneOf({self._values})'
-
-
-class OWLDataSomeValuesFrom(OWLQuantifiedDataRestriction):
-    """Represents a DataSomeValuesFrom restriction in the OWL 2 Specification."""
-    __slots__ = '_property'
-
-    type_index: Final = 3012
-
-    _property: OWLDataPropertyExpression
-
-    def __init__(self, property: OWLDataPropertyExpression, filler: OWLDataRange):
-        """Gets an OWLDataSomeValuesFrom restriction.
-
-        Args:
-            property: The data property that the restriction acts along.
-            filler: The data range that is the filler.
-
-        Returns:
-            An OWLDataSomeValuesFrom restriction along the specified property with the specified filler.
-        """
-        super().__init__(filler)
-        self._property = property
-
-    def __repr__(self):
-        return f"OWLDataSomeValuesFrom(property={repr(self._property)},filler={repr(self._filler)})"
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._filler == other._filler and self._property == other._property
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._filler, self._property))
-
-    def get_property(self) -> OWLDataPropertyExpression:
-        # documented in parent
-        return self._property
-
-
-class OWLNaryDataRange(OWLDataRange, HasOperands[OWLDataRange]):
-    """OWLNaryDataRange."""
-    __slots__ = ()
-
-    _operands: Sequence[OWLDataRange]
-
-    def __init__(self, operands: Iterable[OWLDataRange]):
-        """
-        Args:
-            operands: Data ranges.
-        """
-        self._operands = tuple(operands)
-
-    def operands(self) -> Iterable[OWLDataRange]:
-        # documented in parent
-        yield from self._operands
-
-    def __repr__(self):
-        return f'{type(self).__name__}({repr(self._operands)})'
-
-    def __eq__(self, other):
-        if type(other) == type(self):
-            return self._operands == other._operands
-        return NotImplemented
-
-    def __hash__(self):
-        return hash(self._operands)
-
-
-class OWLDataUnionOf(OWLNaryDataRange):
-    """Represents a DataUnionOf data range in the OWL 2 Specification."""
-    __slots__ = '_operands'
-    type_index: Final = 4005
-
-    _operands: Sequence[OWLDataRange]
-
-
-class OWLDataIntersectionOf(OWLNaryDataRange):
-    """Represents DataIntersectionOf  in the OWL 2 Specification."""
-    __slots__ = '_operands'
-    type_index: Final = 4004
-
-    _operands: Sequence[OWLDataRange]
-
-
-class OWLImportsDeclaration(HasIRI):
-    """Represents an import statement in an ontology."""
-    __slots__ = '_iri'
-
-    def __init__(self, import_iri: IRI):
-        """
-        Args:
-            import_import_iri: Imported ontology.
-
-        Returns:
-            An imports declaration.
-        """
-        self._iri = import_iri
-
-    def get_iri(self) -> IRI:
-        """Gets the import IRI.
-
-        Returns:
-            The import IRI that points to the ontology to be imported. The imported ontology might have this IRI as
-            its ontology IRI but this is not mandated. For example, an ontology with a non-resolvable ontology IRI
-            can be deployed at a resolvable URL.
-        """
-        return self._iri
-
-
-class OWLLogicalAxiom(OWLAxiom, metaclass=ABCMeta):
-    """A base interface of all axioms that affect the logical meaning of an ontology. This excludes declaration axioms
-    (including imports declarations) and annotation axioms.
-    """
-    __slots__ = ()
-
-    def __init__(self, annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(annotations=annotations)
-
-    def is_logical_axiom(self) -> bool:
-        return True
-
-
-class OWLPropertyAxiom(OWLLogicalAxiom, metaclass=ABCMeta):
-    """The base interface for property axioms."""
-    __slots__ = ()
-
-    def __init__(self, annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(annotations=annotations)
-
-
-class OWLObjectPropertyAxiom(OWLPropertyAxiom, metaclass=ABCMeta):
-    """The base interface for object property axioms."""
-    __slots__ = ()
-
-
-class OWLDataPropertyAxiom(OWLPropertyAxiom, metaclass=ABCMeta):
-    """The base interface for data property axioms."""
-    __slots__ = ()
-
-
-class OWLIndividualAxiom(OWLLogicalAxiom, metaclass=ABCMeta):
-    """The base interface for individual axioms."""
-    __slots__ = ()
-
-    def __init__(self, annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(annotations=annotations)
-
-
-class OWLClassAxiom(OWLLogicalAxiom, metaclass=ABCMeta):
-    """The base interface for class axioms."""
-    __slots__ = ()
-
-    def __init__(self, annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(annotations=annotations)
-
-
-class OWLDeclarationAxiom(OWLAxiom):
-    """Represents a Declaration axiom in the OWL 2 Specification. A declaration axiom declares an entity in an ontology.
-       It doesn't affect the logical meaning of the ontology."""
-    __slots__ = '_entity'
-
-    _entity: OWLEntity
-
-    def __init__(self, entity: OWLEntity, annotations: Optional[Iterable['OWLAnnotation']] = None):
-        self._entity = entity
-        super().__init__(annotations=annotations)
-
-    def get_entity(self) -> OWLEntity:
-        return self._entity
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._entity == other._entity and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._entity, *self._annotations))
-
-    def __repr__(self):
-        return f'OWLDeclarationAxiom(entity={self._entity},annotations={self._annotations})'
-
-
-class OWLDatatypeDefinitionAxiom(OWLLogicalAxiom):
-    """Represents a DatatypeDefinition axiom in the OWL 2 Specification."""
-    __slots__ = '_datatype', '_datarange'
-
-    _datatype: OWLDatatype
-    _datarange: OWLDataRange
-
-    def __init__(self, datatype: OWLDatatype, datarange: OWLDataRange,
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        self._datatype = datatype
-        self._datarange = datarange
-        super().__init__(annotations=annotations)
-
-    def get_datatype(self) -> OWLDatatype:
-        return self._datatype
-
-    def get_datarange(self) -> OWLDataRange:
-        return self._datarange
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._datatype == other._datatype and self._datarange == other._datarange \
-                   and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._datatype, self._datarange, *self._annotations))
-
-    def __repr__(self):
-        return f'OWLDatatypeDefinitionAxiom(datatype={self._datatype},datarange={self._datarange},' \
-               f'annotations={self._annotations})'
-
-
-class OWLHasKeyAxiom(OWLLogicalAxiom, HasOperands[OWLPropertyExpression]):
-    """Represents a HasKey axiom in the OWL 2 Specification."""
-    __slots__ = '_class_expression', '_property_expressions'
-
-    _class_expression: OWLClassExpression
-    _property_expressions: List[OWLPropertyExpression]
-
-    def __init__(self, class_expression: OWLClassExpression, property_expressions: List[OWLPropertyExpression],
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        self._class_expression = class_expression
-        self._property_expressions = property_expressions
-        super().__init__(annotations=annotations)
-
-    def get_class_expression(self) -> OWLClassExpression:
-        return self._class_expression
-
-    def get_property_expressions(self) -> List[OWLPropertyExpression]:
-        return self._property_expressions
-
-    def operands(self) -> Iterable[OWLPropertyExpression]:
-        yield from self._property_expressions
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._class_expression == other._class_expression \
-                   and self._property_expressions == other._property_expressions \
-                   and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._class_expression, *self._property_expressions, *self._annotations))
-
-    def __repr__(self):
-        return f'OWLHasKeyAxiom(class_expression={self._class_expression},' \
-               f'property_expressions={self._property_expressions},annotations={self._annotations})'
-
-
-class OWLNaryAxiom(Generic[_C], OWLAxiom, metaclass=ABCMeta):
-    """Represents an axiom that contains two or more operands that could also be represented with multiple pairwise
-    axioms.
-
-    Args:
-        _C: Class of contained objects.
-    """
-    __slots__ = ()
-
-    @abstractmethod
-    def as_pairwise_axioms(self) -> Iterable['OWLNaryAxiom[_C]']:
-        pass
-
-
-# noinspection PyUnresolvedReferences
-# noinspection PyDunderSlots
-class OWLNaryClassAxiom(OWLClassAxiom, OWLNaryAxiom[OWLClassExpression], metaclass=ABCMeta):
-    """Represents an axiom that contains two or more operands that could also be represented with
-        multiple pairwise axioms."""
-    __slots__ = '_class_expressions'
-    _class_expressions: List[OWLClassExpression]
-
-    @abstractmethod
-    def __init__(self, class_expressions: List[OWLClassExpression],
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        self._class_expressions = [*class_expressions]
-        super().__init__(annotations=annotations)
-
-    def class_expressions(self) -> Iterable[OWLClassExpression]:
-        """Gets all of the top level class expressions that appear in this axiom.
-
-        Returns:
-            Sorted stream of class expressions that appear in the axiom.
-        """
-        yield from self._class_expressions
-
-    def as_pairwise_axioms(self) -> Iterable['OWLNaryClassAxiom']:
-        """Gets this axiom as a set of pairwise axioms; if the axiom contains only two operands,
-        the axiom itself is returned unchanged, including its annotations.
-
-        Returns:
-            This axiom as a set of pairwise axioms.
-        """
-        if len(self._class_expressions) < 3:
-            yield self
-        else:
-            yield from map(type(self), combinations(self._class_expressions, 2))
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._class_expressions == other._class_expressions and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((*self._class_expressions, *self._annotations))
-
-    def __repr__(self):
-        return f'{type(self).__name__}({self._class_expressions},{self._annotations})'
-
-
-class OWLEquivalentClassesAxiom(OWLNaryClassAxiom):
-    """Represents an EquivalentClasses axiom in the OWL 2 Specification."""
-    __slots__ = ()
-
-    def __init__(self, class_expressions: List[OWLClassExpression],
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(class_expressions=class_expressions, annotations=annotations)
-
-    def contains_named_equivalent_class(self) -> bool:
-        return any(isinstance(ce, OWLClass) for ce in self._class_expressions)
-
-    def contains_owl_nothing(self) -> bool:
-        return any(isinstance(ce, OWLNothing) for ce in self._class_expressions)
-
-    def contains_owl_thing(self) -> bool:
-        return any(isinstance(ce, OWLThing) for ce in self._class_expressions)
-
-    def named_classes(self) -> Iterable[OWLClass]:
-        yield from (ce for ce in self._class_expressions if isinstance(ce, OWLClass))
-
-
-class OWLDisjointClassesAxiom(OWLNaryClassAxiom):
-    """Represents a DisjointClasses axiom in the OWL 2 Specification."""
-    __slots__ = ()
-
-    def __init__(self, class_expressions: List[OWLClassExpression],
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(class_expressions=class_expressions, annotations=annotations)
-
-
-class OWLNaryIndividualAxiom(OWLIndividualAxiom, OWLNaryAxiom[OWLIndividual], metaclass=ABCMeta):
-    """Represents an axiom that contains two or more operands that could also be represented with
-            multiple pairwise individual axioms."""
-    __slots__ = '_individuals'
-
-    _individuals: List[OWLIndividual]
-
-    @abstractmethod
-    def __init__(self, individuals: List[OWLIndividual],
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        self._individuals = [*individuals]
-        super().__init__(annotations=annotations)
-
-    def individuals(self) -> Iterable[OWLIndividual]:
-        """Get the individuals.
-
-        Returns:
-            Generator containing the individuals.
-        """
-        yield from self._individuals
-
-    def as_pairwise_axioms(self) -> Iterable['OWLNaryIndividualAxiom']:
-        if len(self._individuals) < 3:
-            yield self
-        else:
-            yield from map(type(self), combinations(self._individuals, 2))
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._individuals == other._individuals and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((*self._individuals, *self._annotations))
-
-    def __repr__(self):
-        return f'{type(self).__name__}({self._individuals},{self._annotations})'
-
-
-class OWLDifferentIndividualsAxiom(OWLNaryIndividualAxiom):
-    """Represents a DifferentIndividuals axiom in the OWL 2 Specification."""
-    __slots__ = ()
-
-    def __init__(self, individuals: List[OWLIndividual],
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(individuals=individuals, annotations=annotations)
-
-
-class OWLSameIndividualAxiom(OWLNaryIndividualAxiom):
-    """Represents a SameIndividual axiom in the OWL 2 Specification."""
-    __slots__ = ()
-
-    def __init__(self, individuals: List[OWLIndividual],
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(individuals=individuals, annotations=annotations)
-
-
-class OWLNaryPropertyAxiom(Generic[_P], OWLPropertyAxiom, OWLNaryAxiom[_P], metaclass=ABCMeta):
-    """Represents an axiom that contains two or more operands that could also be represented with
-       multiple pairwise property axioms."""
-    __slots__ = '_properties'
-
-    _properties: List[_P]
-
-    @abstractmethod
-    def __init__(self, properties: List[_P], annotations: Optional[Iterable['OWLAnnotation']] = None):
-        self._properties = [*properties]
-        super().__init__(annotations=annotations)
-
-    def properties(self) -> Iterable[_P]:
-        """Get all the properties that appear in the axiom.
-
-        Returns:
-            Generator containing the properties.
-        """
-        yield from self._properties
-
-    def as_pairwise_axioms(self) -> Iterable['OWLNaryPropertyAxiom']:
-        if len(self._properties) < 3:
-            yield self
-        else:
-            yield from map(type(self), combinations(self._properties, 2))
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._properties == other._properties and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((*self._properties, *self._annotations))
-
-    def __repr__(self):
-        return f'{type(self).__name__}({self._properties},{self._annotations})'
-
-
-class OWLEquivalentObjectPropertiesAxiom(OWLNaryPropertyAxiom[OWLObjectPropertyExpression], OWLObjectPropertyAxiom):
-    """Represents EquivalentObjectProperties axioms in the OWL 2 Specification."""
-    __slots__ = ()
-
-    def __init__(self, properties: List[OWLObjectPropertyExpression],
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(properties=properties, annotations=annotations)
-
-
-class OWLDisjointObjectPropertiesAxiom(OWLNaryPropertyAxiom[OWLObjectPropertyExpression], OWLObjectPropertyAxiom):
-    """Represents DisjointObjectProperties axioms in the OWL 2 Specification."""
-    __slots__ = ()
-
-    def __init__(self, properties: List[OWLObjectPropertyExpression],
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(properties=properties, annotations=annotations)
-
-
-class OWLInverseObjectPropertiesAxiom(OWLNaryPropertyAxiom[OWLObjectPropertyExpression], OWLObjectPropertyAxiom):
-    """Represents InverseObjectProperties axioms in the OWL 2 Specification."""
-    __slots__ = '_first', '_second'
-
-    _first: OWLObjectPropertyExpression
-    _second: OWLObjectPropertyExpression
-
-    def __init__(self, first: OWLObjectPropertyExpression, second: OWLObjectPropertyExpression,
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        self._first = first
-        self._second = second
-        super().__init__(properties=[first, second], annotations=annotations)
-
-    def get_first_property(self) -> OWLObjectPropertyExpression:
-        return self._first
-
-    def get_second_property(self) -> OWLObjectPropertyExpression:
-        return self._second
-
-    def __repr__(self):
-        return f'OWLInverseObjectPropertiesAxiom(first={self._first},second={self._second},' \
-               f'annotations={self._annotations})'
-
-
-class OWLEquivalentDataPropertiesAxiom(OWLNaryPropertyAxiom[OWLDataPropertyExpression], OWLDataPropertyAxiom):
-    """Represents EquivalentDataProperties axioms in the OWL 2 Specification."""
-    __slots__ = ()
-
-    def __init__(self, properties: List[OWLDataPropertyExpression],
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(properties=properties, annotations=annotations)
-
-
-class OWLDisjointDataPropertiesAxiom(OWLNaryPropertyAxiom[OWLDataPropertyExpression], OWLDataPropertyAxiom):
-    """Represents DisjointDataProperties axioms in the OWL 2 Specification."""
-    __slots__ = ()
-
-    def __init__(self, properties: List[OWLDataPropertyExpression],
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(properties=properties, annotations=annotations)
-
-
-class OWLSubClassOfAxiom(OWLClassAxiom):
-    """Represents an SubClassOf axiom in the OWL 2 Specification."""
-    __slots__ = '_sub_class', '_super_class'
-
-    _sub_class: OWLClassExpression
-    _super_class: OWLClassExpression
-
-    def __init__(self, sub_class: OWLClassExpression, super_class: OWLClassExpression,
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        """Get an equivalent classes axiom with specified operands and no annotations.
-
-        Args:
-            sub_class: The sub-class.
-            super_class: The super class.
-            annotations: Annotations.
-        """
-        self._sub_class = sub_class
-        self._super_class = super_class
-        super().__init__(annotations=annotations)
-
-    def get_sub_class(self) -> OWLClassExpression:
-        return self._sub_class
-
-    def get_super_class(self) -> OWLClassExpression:
-        return self._super_class
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._super_class == other._super_class and self._sub_class == other._sub_class \
-                   and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._super_class, self._sub_class, *self._annotations))
-
-    def __repr__(self):
-        return f'OWLSubClassOfAxiom(sub_class={self._sub_class},super_class={self._super_class},' \
-               f'annotations={self._annotations})'
-
-
-class OWLDisjointUnionAxiom(OWLClassAxiom):
-    """Represents a DisjointUnion axiom in the OWL 2 Specification."""
-    __slots__ = '_cls', '_class_expressions'
-
-    _cls: OWLClass
-    _class_expressions: List[OWLClassExpression]
-
-    def __init__(self, cls_: OWLClass, class_expressions: List[OWLClassExpression],
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        self._cls = cls_
-        self._class_expressions = class_expressions
-        super().__init__(annotations=annotations)
-
-    def get_owl_class(self) -> OWLClass:
-        return self._cls
-
-    def get_class_expressions(self) -> Iterable[OWLClassExpression]:
-        yield from self._class_expressions
-
-    def get_owl_equivalent_classes_axiom(self) -> OWLEquivalentClassesAxiom:
-        return OWLEquivalentClassesAxiom(self._cls, OWLObjectUnionOf(self._class_expressions))
-
-    def get_owl_disjoint_classes_axiom(self) -> OWLDisjointClassesAxiom:
-        return OWLDisjointClassesAxiom(self._class_expressions)
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._cls == other._cls and self._class_expressions == other._class_expressions \
-                   and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._cls, *self._class_expressions, *self._annotations))
-
-    def __repr__(self):
-        return f'OWLDisjointUnionAxiom(class={self._cls},class_expressions={self._class_expressions},' \
-               f'annotations={self._annotations})'
-
-
-class OWLClassAssertionAxiom(OWLIndividualAxiom):
-    """Represents ClassAssertion axioms in the OWL 2 Specification."""
-    __slots__ = '_individual', '_class_expression'
-
-    _individual: OWLIndividual
-    _class_expression: OWLClassExpression
-
-    def __init__(self, individual: OWLIndividual, class_expression: OWLClassExpression,
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        """Get a ClassAssertion axiom for the specified individual and class expression.
-        Args:
-            individual: The individual.
-            class_expression: The class the individual belongs to.
-            annotations: Annotations.
-        """
-        self._individual = individual
-        self._class_expression = class_expression
-        super().__init__(annotations=annotations)
-
-    def get_individual(self) -> OWLIndividual:
-        return self._individual
-
-    def get_class_expression(self) -> OWLClassExpression:
-        return self._class_expression
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._class_expression == other._class_expression and self._individual == other._individual \
-                   and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._individual, self._class_expression, *self._annotations))
-
-    def __repr__(self):
-        return f'OWLClassAssertionAxiom(individual={self._individual},class_expression={self._class_expression},' \
-               f'annotations={self._annotations})'
-
-
-class OWLAnnotationAxiom(OWLAxiom, metaclass=ABCMeta):
-    """A super interface for annotation axioms."""
-    __slots__ = ()
-
-    def is_annotation_axiom(self) -> bool:
-        return True
-
-
-class OWLAnnotationProperty(OWLProperty):
-    """Represents an AnnotationProperty in the OWL 2 specification."""
-    __slots__ = '_iri'
-
-    _iri: IRI
-
-    def __init__(self, iri: IRI):
-        """Get a new OWLAnnotationProperty object.
-
-        Args:
-            iri: New OWLAnnotationProperty IRI.
-        """
-        self._iri = iri
-
-    def get_iri(self) -> IRI:
-        # documented in parent
         return self._iri
 
 
-class OWLAnnotation(OWLObject):
-    """Annotations are used in the various types of annotation axioms, which bind annotations to their subjects
-    (i.e. axioms or declarations)."""
-    __slots__ = '_property', '_value'
-
-    _property: OWLAnnotationProperty
-    _value: OWLAnnotationValue
-
-    def __init__(self, property: OWLAnnotationProperty, value: OWLAnnotationValue):
-        """Gets an annotation.
-
-        Args:
-            property: the annotation property.
-            value: The annotation value.
-        """
-        self._property = property
-        self._value = value
-
-    def get_property(self) -> OWLAnnotationProperty:
-        """Gets the property that this annotation acts along.
-
-        Returns:
-            The annotation property.
-        """
-        return self._property
-
-    def get_value(self) -> OWLAnnotationValue:
-        """Gets the annotation value. The type of value will depend upon the type of the annotation e.g. whether the
-        annotation is an OWLLiteral, an IRI or an OWLAnonymousIndividual.
-
-        Returns:
-            The annotation value.
-        """
-        return self._value
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._property == other._property and self._value == other._value
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._property, self._value))
-
-    def __repr__(self):
-        return f'OWLAnnotation({self._property}, {self._value})'
-
-
-class OWLAnnotationAssertionAxiom(OWLAnnotationAxiom):
-    """Represents AnnotationAssertion axioms in the OWL 2 specification."""
-    __slots__ = '_subject', '_annotation'
-
-    _subject: OWLAnnotationSubject
-    _annotation: OWLAnnotation
-
-    def __init__(self, subject: OWLAnnotationSubject, annotation: OWLAnnotation):
-        """Get an annotation assertion axiom - with annotations.
-
-        Args:
-            subject: Subject.
-            annotation: Annotation.
-        """
-        assert isinstance(subject, OWLAnnotationSubject)
-        assert isinstance(annotation, OWLAnnotation)
-
-        self._subject = subject
-        self._annotation = annotation
-
-    def get_subject(self) -> OWLAnnotationSubject:
-        """Gets the subject of this object.
-
-        Returns:
-            The subject.
-        """
-        return self._subject
-
-    def get_property(self) -> OWLAnnotationProperty:
-        """Gets the property.
-
-        Returns:
-            The property.
-        """
-        return self._annotation.get_property()
-
-    def get_value(self) -> OWLAnnotationValue:
-        """Gets the annotation value. This is either an IRI, an OWLAnonymousIndividual or an OWLLiteral.
-
-        Returns:
-            The annotation value.
-        """
-        return self._annotation.get_value()
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._subject == other._subject and self._annotation == other._annotation
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._subject, self._annotation))
-
-    def __repr__(self):
-        return f'OWLAnnotationAssertionAxiom({self._subject}, {self._annotation})'
-
-
-class OWLSubAnnotationPropertyOfAxiom(OWLAnnotationAxiom):
-    """Represents an SubAnnotationPropertyOf axiom in the OWL 2 specification."""
-    __slots__ = '_sub_property', '_super_property'
-
-    _sub_property: OWLAnnotationProperty
-    _super_property: OWLAnnotationProperty
-
-    def __init__(self, sub_property: OWLAnnotationProperty, super_property: OWLAnnotationProperty,
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        self._sub_property = sub_property
-        self._super_property = super_property
-        super().__init__(annotations=annotations)
-
-    def get_sub_property(self) -> OWLAnnotationProperty:
-        return self._sub_property
-
-    def get_super_property(self) -> OWLAnnotationProperty:
-        return self._super_property
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._sub_property == other._sub_property and self._super_property == other._super_property \
-                   and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._sub_property, self._super_property, *self._annotations))
-
-    def __repr__(self):
-        return f'OWLSubAnnotationPropertyOfAxiom(sub_property={self._sub_property},' \
-               f'super_property={self._super_property},annotations={self._annotations})'
-
-
-class OWLAnnotationPropertyDomainAxiom(OWLAnnotationAxiom):
-    """Represents an AnnotationPropertyDomain axiom in the OWL 2 specification."""
-    __slots__ = '_property', '_domain'
-
-    _property: OWLAnnotationProperty
-    _domain: IRI
-
-    def __init__(self, property_: OWLAnnotationProperty, domain: IRI,
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        self._property = property_
-        self._domain = domain
-        super().__init__(annotations=annotations)
-
-    def get_property(self) -> OWLAnnotationProperty:
-        return self._property
-
-    def get_domain(self) -> IRI:
-        return self._domain
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._property == other._property and self._domain == other._domain \
-                   and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._property, self._domain, *self._annotations))
-
-    def __repr__(self):
-        return f'OWLAnnotationPropertyDomainAxiom({repr(self._property)},{repr(self._domain)},' \
-               f'{repr(self._annotations)})'
-
-
-class OWLAnnotationPropertyRangeAxiom(OWLAnnotationAxiom):
-    """Represents an AnnotationPropertyRange axiom in the OWL 2 specification."""
-    __slots__ = '_property', '_range'
-
-    _property: OWLAnnotationProperty
-    _range: IRI
-
-    def __init__(self, property_: OWLAnnotationProperty, range_: IRI,
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        self._property = property_
-        self._range = range_
-        super().__init__(annotations=annotations)
-
-    def get_property(self) -> OWLAnnotationProperty:
-        return self._property
-
-    def get_range(self) -> IRI:
-        return self._range
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._property == other._property and self._range == other._range \
-                   and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._property, self._range, *self._annotations))
-
-    def __repr__(self):
-        return f'OWLAnnotationPropertyRangeAxiom({repr(self._property)},{repr(self._range)},' \
-               f'{repr(self._annotations)})'
-
-
-class OWLSubPropertyAxiom(Generic[_P], OWLPropertyAxiom):
-    """
-    Base interface for object and data sub-property axioms.
-    """
-    __slots__ = '_sub_property', '_super_property'
-
-    _sub_property: _P
-    _super_property: _P
-
-    @abstractmethod
-    def __init__(self, sub_property: _P, super_property: _P,
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        self._sub_property = sub_property
-        self._super_property = super_property
-        super().__init__(annotations=annotations)
-
-    def get_sub_property(self) -> _P:
-        return self._sub_property
-
-    def get_super_property(self) -> _P:
-        return self._super_property
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._sub_property == other._sub_property and self._super_property == other._super_property \
-                   and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._sub_property, self._super_property, *self._annotations))
-
-    def __repr__(self):
-        return f'{type(self).__name__}(sub_property={self._sub_property},super_property={self._super_property},' \
-               f'annotations={self._annotations})'
-
-
-class OWLSubObjectPropertyOfAxiom(OWLSubPropertyAxiom[OWLObjectPropertyExpression], OWLObjectPropertyAxiom):
-    """Represents a SubObjectPropertyOf axiom in the OWL 2 specification."""
-    __slots__ = ()
-
-    def __init__(self, sub_property: OWLObjectPropertyExpression, super_property: OWLObjectPropertyExpression,
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(sub_property=sub_property, super_property=super_property, annotations=annotations)
-
-
-class OWLSubDataPropertyOfAxiom(OWLSubPropertyAxiom[OWLDataPropertyExpression], OWLDataPropertyAxiom):
-    """Represents a SubDataPropertyOf axiom in the OWL 2 specification."""
-    __slots__ = ()
-
-    def __init__(self, sub_property: OWLDataPropertyExpression, super_property: OWLDataPropertyExpression,
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(sub_property=sub_property, super_property=super_property, annotations=annotations)
-
-
-class OWLPropertyAssertionAxiom(Generic[_P, _C], OWLIndividualAxiom, metaclass=ABCMeta):
-    """Represents a PropertyAssertion axiom in the OWL 2 specification."""
-    __slots__ = '_subject', '_property', '_object'
-
-    _subject: OWLIndividual
-    _property: _P
-    _object: _C
-
-    @abstractmethod
-    def __init__(self, subject: OWLIndividual, property_: _P, object_: _C,
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        """Get a PropertyAssertion axiom for the specified subject, property, object.
-        Args:
-            subject: The subject of the property assertion.
-            property_: The property of the property assertion.
-            object_: The object of the property assertion.
-            annotations: Annotations.
-        """
-        assert isinstance(subject, OWLIndividual)
-
-        self._subject = subject
-        self._property = property_
-        self._object = object_
-        super().__init__(annotations=annotations)
-
-    def get_subject(self) -> OWLIndividual:
-        return self._subject
-
-    def get_property(self) -> _P:
-        return self._property
-
-    def get_object(self) -> _C:
-        return self._object
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._subject == other._subject and self._property == other._property and \
-                   self._object == other._object and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._subject, self._property, self._object, *self._annotations))
-
-    def __repr__(self):
-        return f'{type(self).__name__}(subject={self._subject},property={self._property},' \
-               f'object={self._object},annotation={self._annotations})'
-
-
-class OWLObjectPropertyAssertionAxiom(OWLPropertyAssertionAxiom[OWLObjectPropertyExpression, OWLIndividual]):
-    """Represents an ObjectPropertyAssertion axiom in the OWL 2 specification."""
-    __slots__ = ()
-
-    def __init__(self, subject: OWLIndividual, property_: OWLObjectPropertyExpression, object_: OWLIndividual,
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(subject, property_, object_, annotations)
-
-
-class OWLNegativeObjectPropertyAssertionAxiom(OWLPropertyAssertionAxiom[OWLObjectPropertyExpression, OWLIndividual]):
-    """Represents a NegativeObjectPropertyAssertion axiom in the OWL 2 specification."""
-    __slots__ = ()
-
-    def __init__(self, subject: OWLIndividual, property_: OWLObjectPropertyExpression, object_: OWLIndividual,
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(subject, property_, object_, annotations)
-
-
-class OWLDataPropertyAssertionAxiom(OWLPropertyAssertionAxiom[OWLDataPropertyExpression, OWLLiteral]):
-    """Represents an DataPropertyAssertion axiom in the OWL 2 specification."""
-    __slots__ = ()
-
-    def __init__(self, subject: OWLIndividual, property_: OWLDataPropertyExpression, object_: OWLLiteral,
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(subject, property_, object_, annotations)
-
-
-class OWLNegativeDataPropertyAssertionAxiom(OWLPropertyAssertionAxiom[OWLDataPropertyExpression, OWLLiteral]):
-    """Represents an NegativeDataPropertyAssertion axiom in the OWL 2 specification."""
-    __slots__ = ()
-
-    def __init__(self, subject: OWLIndividual, property_: OWLDataPropertyExpression, object_: OWLLiteral,
-                 annotations: Optional[Iterable['OWLAnnotation']] = None):
-        super().__init__(subject, property_, object_, annotations)
-
-
-class OWLUnaryPropertyAxiom(Generic[_P], OWLPropertyAxiom, metaclass=ABCMeta):
-    """Unary property axiom."""
-    __slots__ = '_property'
-
-    _property: _P
-
-    def __init__(self, property_: _P, annotations: Optional[Iterable[OWLAnnotation]] = None):
-        self._property = property_
-        super().__init__(annotations=annotations)
-
-    def get_property(self) -> _P:
-        return self._property
-
-
-class OWLObjectPropertyCharacteristicAxiom(OWLUnaryPropertyAxiom[OWLObjectPropertyExpression],
-                                           OWLObjectPropertyAxiom, metaclass=ABCMeta):
-    """Base interface for functional object property axiom."""
-    __slots__ = ()
-
-    @abstractmethod
-    def __init__(self, property_: OWLObjectPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
-        super().__init__(property_=property_, annotations=annotations)
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._property == other._property and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._property, *self._annotations))
-
-    def __repr__(self):
-        return f"{type(self).__name__}({repr(self._property)},{repr(self._annotations)})"
-
-
-class OWLFunctionalObjectPropertyAxiom(OWLObjectPropertyCharacteristicAxiom):
-    """Represents FunctionalObjectProperty axioms in the OWL 2 specification."""
-    __slots__ = ()
-
-    def __init__(self, property_: OWLObjectPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
-        super().__init__(property_=property_, annotations=annotations)
-
-
-class OWLAsymmetricObjectPropertyAxiom(OWLObjectPropertyCharacteristicAxiom):
-    """Represents AsymmetricObjectProperty axioms in the OWL 2 specification."""
-    __slots__ = ()
-
-    def __init__(self, property_: OWLObjectPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
-        super().__init__(property_=property_, annotations=annotations)
-
-
-class OWLInverseFunctionalObjectPropertyAxiom(OWLObjectPropertyCharacteristicAxiom):
-    """Represents InverseFunctionalObjectProperty axioms in the OWL 2 specification."""
-    __slots__ = ()
-
-    def __init__(self, property_: OWLObjectPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
-        super().__init__(property_=property_, annotations=annotations)
-
-
-class OWLIrreflexiveObjectPropertyAxiom(OWLObjectPropertyCharacteristicAxiom):
-    """Represents IrreflexiveObjectProperty axioms in the OWL 2 specification."""
-    __slots__ = ()
-
-    def __init__(self, property_: OWLObjectPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
-        super().__init__(property_=property_, annotations=annotations)
-
-
-class OWLReflexiveObjectPropertyAxiom(OWLObjectPropertyCharacteristicAxiom):
-    """Represents ReflexiveObjectProperty axioms in the OWL 2 specification."""
-    __slots__ = ()
-
-    def __init__(self, property_: OWLObjectPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
-        super().__init__(property_=property_, annotations=annotations)
-
-
-class OWLSymmetricObjectPropertyAxiom(OWLObjectPropertyCharacteristicAxiom):
-    """Represents SymmetricObjectProperty axioms in the OWL 2 specification."""
-    __slots__ = ()
-
-    def __init__(self, property_: OWLObjectPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
-        super().__init__(property_=property_, annotations=annotations)
-
-
-class OWLTransitiveObjectPropertyAxiom(OWLObjectPropertyCharacteristicAxiom):
-    """Represents TransitiveObjectProperty axioms in the OWL 2 specification."""
-    __slots__ = ()
-
-    def __init__(self, property_: OWLObjectPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
-        super().__init__(property_=property_, annotations=annotations)
-
-
-class OWLDataPropertyCharacteristicAxiom(OWLUnaryPropertyAxiom[OWLDataPropertyExpression],
-                                         OWLDataPropertyAxiom, metaclass=ABCMeta):
-    """Base interface for Functional data property axiom."""
-    __slots__ = ()
-
-    @abstractmethod
-    def __init__(self, property_: OWLDataPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
-        super().__init__(property_=property_, annotations=annotations)
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._property == other._property and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._property, *self._annotations))
-
-    def __repr__(self):
-        return f"{type(self).__name__}({repr(self._property)},{repr(self._annotations)})"
-
-
-class OWLFunctionalDataPropertyAxiom(OWLDataPropertyCharacteristicAxiom):
-    """Represents FunctionalDataProperty axioms in the OWL 2 specification."""
-    __slots__ = ()
-
-    def __init__(self, property_: OWLDataPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
-        super().__init__(property_=property_, annotations=annotations)
-
-
-class OWLPropertyDomainAxiom(Generic[_P], OWLUnaryPropertyAxiom[_P], metaclass=ABCMeta):
-    """Represents ObjectPropertyDomain axioms in the OWL 2 specification."""
-    __slots__ = '_domain'
-
-    _domain: OWLClassExpression
-
-    @abstractmethod
-    def __init__(self, property_: _P, domain: OWLClassExpression,
-                 annotations: Optional[Iterable[OWLAnnotation]] = None):
-        self._domain = domain
-        super().__init__(property_=property_, annotations=annotations)
-
-    def get_domain(self) -> OWLClassExpression:
-        return self._domain
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._property == other._property and self._domain == other._domain \
-                   and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._property, self._domain, *self._annotations))
-
-    def __repr__(self):
-        return f"{type(self).__name__}({repr(self._property)},{repr(self._domain)},{repr(self._annotations)})"
-
-
-class OWLPropertyRangeAxiom(Generic[_P, _R], OWLUnaryPropertyAxiom[_P], metaclass=ABCMeta):
-    """Represents ObjectPropertyRange axioms in the OWL 2 specification."""
-    __slots__ = '_range'
-
-    _range: _R
-
-    @abstractmethod
-    def __init__(self, property_: _P, range_: _R, annotations: Optional[Iterable[OWLAnnotation]] = None):
-        self._range = range_
-        super().__init__(property_=property_, annotations=annotations)
-
-    def get_range(self) -> _R:
-        return self._range
-
-    def __eq__(self, other):
-        if type(other) is type(self):
-            return self._property == other._property and self._range == other._range \
-                   and self._annotations == other._annotations
-        return NotImplemented
-
-    def __hash__(self):
-        return hash((self._property, self._range, *self._annotations))
-
-    def __repr__(self):
-        return f"{type(self).__name__}({repr(self._property)},{repr(self._range)},{repr(self._annotations)})"
-
-
-class OWLObjectPropertyDomainAxiom(OWLPropertyDomainAxiom[OWLObjectPropertyExpression]):
-    """ Represents a ObjectPropertyDomain axiom in the OWL 2 Specification."""
-    __slots__ = ()
-
-    def __init__(self, property_: OWLObjectPropertyExpression, domain: OWLClassExpression,
-                 annotations: Optional[Iterable[OWLAnnotation]] = None):
-        super().__init__(property_=property_, domain=domain, annotations=annotations)
-
-
-class OWLDataPropertyDomainAxiom(OWLPropertyDomainAxiom[OWLDataPropertyExpression]):
-    """ Represents a DataPropertyDomain axiom in the OWL 2 Specification."""
-    __slots__ = ()
-
-    def __init__(self, property_: OWLDataPropertyExpression, domain: OWLClassExpression,
-                 annotations: Optional[Iterable[OWLAnnotation]] = None):
-        super().__init__(property_=property_, domain=domain, annotations=annotations)
-
-
-class OWLObjectPropertyRangeAxiom(OWLPropertyRangeAxiom[OWLObjectPropertyExpression, OWLClassExpression]):
-    """ Represents a ObjectPropertyRange axiom in the OWL 2 Specification."""
-    __slots__ = ()
-
-    def __init__(self, property_: OWLObjectPropertyExpression, range_: OWLClassExpression,
-                 annotations: Optional[Iterable[OWLAnnotation]] = None):
-        super().__init__(property_=property_, range_=range_, annotations=annotations)
-
-
-class OWLDataPropertyRangeAxiom(OWLPropertyRangeAxiom[OWLDataPropertyExpression, OWLDataRange]):
-    """ Represents a DataPropertyRange axiom in the OWL 2 Specification."""
-    __slots__ = ()
-
-    def __init__(self, property_: OWLDataPropertyExpression, range_: OWLDataRange,
-                 annotations: Optional[Iterable[OWLAnnotation]] = None):
-        super().__init__(property_=property_, range_=range_, annotations=annotations)
-
-
 class OWLOntology(OWLObject, metaclass=ABCMeta):
     """Represents an OWL 2 Ontology  in the OWL 2 specification.
 
@@ -3714,9 +773,8 @@ class expression with respect to the imports closure of the root ontology.
 
 
 """Important constant objects section"""
+# @TODO: Some of them must be removed from here as they are defined under owl literal
 
-OWLThing: Final = OWLClass(OWLRDFVocabulary.OWL_THING.get_iri())  #: : :The OWL Class corresponding to owl:Thing
-OWLNothing: Final = OWLClass(OWLRDFVocabulary.OWL_NOTHING.get_iri())  #: : :The OWL Class corresponding to owl:Nothing
 #: the built in top object property
 OWLTopObjectProperty: Final = OWLObjectProperty(OWLRDFVocabulary.OWL_TOP_OBJECT_PROPERTY.get_iri())
 #: the built in bottom object property
diff --git a/owlapy/model/providers.py b/owlapy/model/providers.py
index e51251bd..3904c467 100644
--- a/owlapy/model/providers.py
+++ b/owlapy/model/providers.py
@@ -1,7 +1,8 @@
 """OWL Datatype restriction constructors."""
 from typing import Union
 from datetime import datetime, date
-from owlapy.model import OWLDatatypeRestriction, OWLFacet, OWLFacetRestriction, OWLLiteral
+from owlapy.owl_literal import OWLLiteral
+from owlapy.class_expression import OWLDatatypeRestriction, OWLFacet, OWLFacetRestriction
 from pandas import Timedelta
 
 Restriction_Literals = Union[OWLLiteral, int, float, Timedelta, datetime, date]
diff --git a/owlapy/owl2sparql/converter.py b/owlapy/owl2sparql/converter.py
index 9b6ef4cc..0bb758d4 100644
--- a/owlapy/owl2sparql/converter.py
+++ b/owlapy/owl2sparql/converter.py
@@ -8,11 +8,12 @@
 from rdflib.plugins.sparql.parser import parseQuery
 
 from owlapy.model import OWLClassExpression, OWLClass, OWLEntity, OWLObjectProperty, \
-    OWLObjectUnionOf, OWLObjectComplementOf, OWLObjectSomeValuesFrom, OWLObjectAllValuesFrom, OWLObjectHasValue, \
+    OWLObjectUnionOf, OWLObjectComplementOf, OWLObjectSomeValuesFrom, OWLObjectAllValuesFrom, \
     OWLNamedIndividual, OWLObjectCardinalityRestriction, OWLObjectMinCardinality, OWLObjectExactCardinality, \
-    OWLObjectMaxCardinality, OWLDataCardinalityRestriction, OWLDataProperty, OWLObjectHasSelf, OWLObjectOneOf, \
-    OWLDataSomeValuesFrom, OWLDataAllValuesFrom, OWLDataHasValue, OWLDatatype, TopOWLDatatype, OWLDataOneOf, \
-    OWLLiteral, OWLDatatypeRestriction, OWLObjectIntersectionOf
+    OWLObjectMaxCardinality, OWLDataCardinalityRestriction, OWLDataProperty, OWLObjectHasSelf, \
+    OWLDataSomeValuesFrom, OWLDataAllValuesFrom, OWLDataHasValue, OWLDatatype, TopOWLDatatype, OWLDataOneOf, OWLObjectIntersectionOf
+from owlapy.class_expression import OWLObjectHasValue, OWLObjectOneOf, OWLDatatypeRestriction
+from owlapy.owl_literal import OWLLiteral
 from owlapy.vocab import OWLFacet, OWLRDFVocabulary
 
 _Variable_facet_comp = MappingProxyType({
@@ -81,6 +82,7 @@ class Owl2SparqlConverter:
     """Convert owl (owlapy model class expressions) to SPARQL."""
     __slots__ = 'ce', 'sparql', 'variables', 'parent', 'parent_var', 'properties', 'variable_entities', 'cnt', \
                 'mapping', 'grouping_vars', 'having_conditions', '_intersection'
+    # @TODO:CD: We need to document this class. The computation behind the mapping is not clear.
 
     ce: OWLClassExpression
     sparql: List[str]
@@ -130,10 +132,6 @@ def convert(self, root_variable: str, ce: OWLClassExpression, named_individuals:
     def modal_depth(self):
         return len(self.variables)
 
-    # @property
-    # def in_intersection(self):
-    #     return self._intersection[self.modal_depth]
-
     @singledispatchmethod
     def render(self, e):
         raise NotImplementedError(e)
@@ -174,13 +172,6 @@ def _maybe_render(self, o):
         else:
             return self.render(o)
 
-    # @contextmanager
-    # def intersection(self):
-    #     self._intersection[self.modal_depth] = True
-    #     try:
-    #         yield
-    #     finally:
-    #         del self._intersection[self.modal_depth]
 
     @contextmanager
     def stack_variable(self, var):
@@ -236,21 +227,6 @@ def _(self, ce: OWLObjectIntersectionOf):
         for op in ce.operands():
             self.process(op)
 
-        # the following part was commented out because it was related to the possible optimization in the complement
-        # operator that has also been commented out
-        # with self.intersection():
-        #     for op in ce.operands():
-        #         self.process(op)
-        #     props = self.properties[self.modal_depth]
-        #     vars_ = set()
-        #     if props:
-        #         for p in props:
-        #             if p in self.mapping:
-        #                 vars_.add(self.mapping[p])
-        #         if len(vars_) == 2:
-        #             v0, v1 = sorted(vars_)
-        #             self.append(f"FILTER ( {v0} != {v1} )")
-
     # an overload of process function
     # this overload is responsible for handling unions of concepts (e.g., Brother ⊔ Sister)
     # general case: C1 ⊔ ... ⊔ Cn
@@ -275,14 +251,6 @@ def _(self, ce: OWLObjectUnionOf):
     @process.register
     def _(self, ce: OWLObjectComplementOf):
         subject = self.current_variable
-        # the conversion was trying here to optimize the query
-        # but the proposed optimization alters the semantics of some queries
-        # example: ( A ⊓ ( B ⊔ ( ¬C ) ) )
-        # with the proposed optimization, the group graph pattern for (¬C) will be { FILTER NOT EXISTS { ?x a C } }
-        # however, the expected pattern is { ?x ?p ?o . FILTER NOT EXISTS { ?x a C } }
-        # the exclusion of "?x ?p ?o" results in the group graph pattern to just return true or false (not bindings)
-        # as a result, we need to comment out the if-clause of the following line
-        # if not self.in_intersection and self.modal_depth == 1:
         self.append_triple(subject, self.mapping.new_individual_variable(), self.mapping.new_individual_variable())
 
         self.append("FILTER NOT EXISTS { ")
@@ -321,19 +289,9 @@ def _(self, ce: OWLObjectAllValuesFrom):
         # filler holds the concept of the expression (Male in our example) and is processed recursively
         filler = ce.get_filler()
 
-        # if the current class expression is the first one we are processing (root of recursion), the following
-        # if-clause tries to restrict the entities (individuals) to consider using owl:NamedIndividual.
-        # However, it is not guaranteed that entities in every KG are instances of owl:NamedIndividual, hence, adding
-        # this triple will affect the results in such cases.
-        # if self.modal_depth == 1:
-        #     self.append_triple(self.current_variable, "a", f"<{OWLRDFVocabulary.OWL_NAMED_INDIVIDUAL.as_str()}>")
 
-        # here, the first group graph pattern starts
-        # the first group graph pattern ensures deals with the entities that appear in a triple with the property
         self.append("{")
-        # if filler.is_owl_thing():
-        #     self.append_triple(self.current_variable, self.mapping.new_property_variable(), object_variable)
-        # else:
+
         if property_expression.is_anonymous():
             # property expression is inverse of a property
             self.append_triple(object_variable, predicate, self.current_variable)
@@ -592,13 +550,17 @@ def as_query(self,
                  ce: OWLClassExpression,
                  count: bool = False,
                  values: Optional[Iterable[OWLNamedIndividual]] = None,
-                 named_individuals: bool = False):
-        # root variable: the variable that will be projected
-        # ce: the class expression to be transformed to a SPARQL query
-        # count: True, counts the results ; False, projects the individuals
-        # values: positive or negative examples from a class expression problem
-        # named_individuals: if set to True, the generated SPARQL query will return only entities that are instances
-        #                    of owl:NamedIndividual
+                 named_individuals: bool = False)->str:
+        """
+        root variable: the variable that will be projected
+        ce: the class expression to be transformed to a SPARQL query
+        count: True, counts the results ; False, projects the individuals
+        values: positive or negative examples from a class expression problem
+        named_individuals: if set to True, the generated SPARQL query will return only entities that are instances
+        of owl:NamedIndividual
+
+        """
+
         qs = ["SELECT"]
         tp = self.convert(root_variable, ce, named_individuals)
         if count:
@@ -614,14 +576,6 @@ def as_query(self,
         qs.extend(tp)
         qs.append(f" }}")
 
-        # group_by_vars = self.grouping_vars[ce]
-        # if group_by_vars:
-        #     qs.append("GROUP BY " + " ".join(sorted(group_by_vars)))
-        # conditions = self.having_conditions[ce]
-        # if conditions:
-        #     qs.append(" HAVING ( ")
-        #     qs.append(" && ".join(sorted(conditions)))
-        #     qs.append(" )")
 
         query = "\n".join(qs)
         parseQuery(query)
@@ -631,9 +585,17 @@ def as_query(self,
 converter = Owl2SparqlConverter()
 
 
-def owl_expression_to_sparql(root_variable: str,
-                             ce: OWLClassExpression,
-                             count: bool = False,
+def owl_expression_to_sparql(root_variable: str = "?x",
+                             expression: OWLClassExpression = None,
                              values: Optional[Iterable[OWLNamedIndividual]] = None,
-                             named_individuals: bool = False):
-    return converter.as_query(root_variable, ce, count, values, named_individuals)
+                             named_individuals: bool = False)->str:
+    """Convert an OWL Class Expression (https://www.w3.org/TR/owl2-syntax/#Class_Expressions) into a SPARQL query
+     root variable: the variable that will be projected
+     expression: the class expression to be transformed to a SPARQL query
+
+     values: positive or negative examples from a class expression problem. Unclear
+     named_individuals: if set to True, the generated SPARQL query will return only entities
+     that are instances of owl:NamedIndividual
+    """
+    assert expression is not None, "expression cannot be None"
+    return converter.as_query(root_variable, expression, False, values, named_individuals)
diff --git a/owlapy/model/_base.py b/owlapy/owl_annotation.py
similarity index 70%
rename from owlapy/model/_base.py
rename to owlapy/owl_annotation.py
index d66a4922..870b855a 100644
--- a/owlapy/model/_base.py
+++ b/owlapy/owl_annotation.py
@@ -1,30 +1,6 @@
-from abc import ABCMeta, abstractmethod
-from typing import Optional, TYPE_CHECKING
-
-if TYPE_CHECKING:
-    from owlapy.model._iri import IRI
-    from owlapy.model import OWLLiteral
-
-
-class OWLObject(metaclass=ABCMeta):
-    """Base interface for OWL objects"""
-    __slots__ = ()
-
-    @abstractmethod
-    def __eq__(self, other):
-        pass
-
-    @abstractmethod
-    def __hash__(self):
-        pass
-
-    @abstractmethod
-    def __repr__(self):
-        pass
-
-    # default
-    def is_anonymous(self) -> bool:
-        return True
+from abc import ABCMeta
+from .owlobject import OWLObject
+from typing import Optional
 
 
 class OWLAnnotationObject(OWLObject, metaclass=ABCMeta):
@@ -47,13 +23,11 @@ def as_anonymous_individual(self):
         """
         return None
 
-
 class OWLAnnotationSubject(OWLAnnotationObject, metaclass=ABCMeta):
     """A marker interface for annotation subjects, which can either be IRIs or anonymous individuals"""
     __slots__ = ()
     pass
 
-
 class OWLAnnotationValue(OWLAnnotationObject, metaclass=ABCMeta):
     """A marker interface for annotation values, which can either be an IRI (URI), Literal or Anonymous Individual."""
     __slots__ = ()
@@ -71,4 +45,4 @@ def as_literal(self) -> Optional['OWLLiteral']:
         Returns:
             if the value is a literal, returns it. Return None otherwise
         """
-        return None
+        return None
\ No newline at end of file
diff --git a/owlapy/owl_axiom.py b/owlapy/owl_axiom.py
new file mode 100644
index 00000000..22135c4a
--- /dev/null
+++ b/owlapy/owl_axiom.py
@@ -0,0 +1,1097 @@
+from abc import ABCMeta, abstractmethod
+
+from typing import TypeVar, List, Optional, Iterable, Generic, Final
+from .owl_property import OWLDataPropertyExpression, OWLObjectPropertyExpression
+from .owlobject import OWLObject, OWLEntity
+from .types import OWLDatatype, OWLDataRange
+from .meta_classes import HasOperands
+from .owl_property import OWLPropertyExpression, OWLProperty
+from .class_expression import OWLClassExpression, OWLClass
+from .owl_individual import OWLIndividual
+from .iri import IRI
+from owlapy.owl_annotation import OWLAnnotationSubject, OWLAnnotationValue
+from .owl_literal import OWLLiteral
+
+_C = TypeVar('_C', bound='OWLObject')  #:
+_P = TypeVar('_P', bound='OWLPropertyExpression')  #:
+_R = TypeVar('_R', bound='OWLPropertyRange')  #:
+
+class OWLAxiom(OWLObject, metaclass=ABCMeta):
+    """Represents Axioms in the OWL 2 Specification.
+
+    An OWL ontology contains a set of axioms. These axioms can be annotation axioms, declaration axioms, imports axioms
+    or logical axioms.
+    """
+    __slots__ = '_annotations'
+
+    _annotations: List['OWLAnnotation']
+
+    def __init__(self, annotations: Optional[Iterable['OWLAnnotation']] = None):
+        self._annotations = list(annotations) if annotations is not None else list()
+
+    def annotations(self) -> Optional[List['OWLAnnotation']]:
+        return self._annotations
+
+    def is_annotated(self) -> bool:
+        return self._annotations is not None and len(self._annotations) > 0
+
+    def is_logical_axiom(self) -> bool:
+        return False
+
+    def is_annotation_axiom(self) -> bool:
+        return False
+    # TODO: XXX
+
+
+class OWLLogicalAxiom(OWLAxiom, metaclass=ABCMeta):
+    """A base interface of all axioms that affect the logical meaning of an ontology. This excludes declaration axioms
+    (including imports declarations) and annotation axioms.
+    """
+    __slots__ = ()
+
+    def __init__(self, annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(annotations=annotations)
+
+    def is_logical_axiom(self) -> bool:
+        return True
+
+
+class OWLPropertyAxiom(OWLLogicalAxiom, metaclass=ABCMeta):
+    """The base interface for property axioms."""
+    __slots__ = ()
+
+    def __init__(self, annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(annotations=annotations)
+
+
+class OWLObjectPropertyAxiom(OWLPropertyAxiom, metaclass=ABCMeta):
+    """The base interface for object property axioms."""
+    __slots__ = ()
+
+
+class OWLDataPropertyAxiom(OWLPropertyAxiom, metaclass=ABCMeta):
+    """The base interface for data property axioms."""
+    __slots__ = ()
+
+
+class OWLIndividualAxiom(OWLLogicalAxiom, metaclass=ABCMeta):
+    """The base interface for individual axioms."""
+    __slots__ = ()
+
+    def __init__(self, annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(annotations=annotations)
+
+
+class OWLClassAxiom(OWLLogicalAxiom, metaclass=ABCMeta):
+    """The base interface for class axioms."""
+    __slots__ = ()
+
+    def __init__(self, annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(annotations=annotations)
+
+
+class OWLDeclarationAxiom(OWLAxiom):
+    """Represents a Declaration axiom in the OWL 2 Specification. A declaration axiom declares an entity in an ontology.
+       It doesn't affect the logical meaning of the ontology."""
+    __slots__ = '_entity'
+
+    _entity: OWLEntity
+
+    def __init__(self, entity: OWLEntity, annotations: Optional[Iterable['OWLAnnotation']] = None):
+        self._entity = entity
+        super().__init__(annotations=annotations)
+
+    def get_entity(self) -> OWLEntity:
+        return self._entity
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._entity == other._entity and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._entity, *self._annotations))
+
+    def __repr__(self):
+        return f'OWLDeclarationAxiom(entity={self._entity},annotations={self._annotations})'
+
+
+class OWLDatatypeDefinitionAxiom(OWLLogicalAxiom):
+    """Represents a DatatypeDefinition axiom in the OWL 2 Specification."""
+    __slots__ = '_datatype', '_datarange'
+
+    _datatype: OWLDatatype
+    _datarange: OWLDataRange
+
+    def __init__(self, datatype: OWLDatatype, datarange: OWLDataRange,
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        self._datatype = datatype
+        self._datarange = datarange
+        super().__init__(annotations=annotations)
+
+    def get_datatype(self) -> OWLDatatype:
+        return self._datatype
+
+    def get_datarange(self) -> OWLDataRange:
+        return self._datarange
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._datatype == other._datatype and self._datarange == other._datarange \
+                and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._datatype, self._datarange, *self._annotations))
+
+    def __repr__(self):
+        return f'OWLDatatypeDefinitionAxiom(datatype={self._datatype},datarange={self._datarange},' \
+               f'annotations={self._annotations})'
+
+
+class OWLHasKeyAxiom(OWLLogicalAxiom, HasOperands[OWLPropertyExpression]):
+    """Represents a HasKey axiom in the OWL 2 Specification."""
+    __slots__ = '_class_expression', '_property_expressions'
+
+    _class_expression: OWLClassExpression
+    _property_expressions: List[OWLPropertyExpression]
+
+    def __init__(self, class_expression: OWLClassExpression, property_expressions: List[OWLPropertyExpression],
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        self._class_expression = class_expression
+        self._property_expressions = property_expressions
+        super().__init__(annotations=annotations)
+
+    def get_class_expression(self) -> OWLClassExpression:
+        return self._class_expression
+
+    def get_property_expressions(self) -> List[OWLPropertyExpression]:
+        return self._property_expressions
+
+    def operands(self) -> Iterable[OWLPropertyExpression]:
+        yield from self._property_expressions
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._class_expression == other._class_expression \
+                and self._property_expressions == other._property_expressions \
+                and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._class_expression, *self._property_expressions, *self._annotations))
+
+    def __repr__(self):
+        return f'OWLHasKeyAxiom(class_expression={self._class_expression},' \
+               f'property_expressions={self._property_expressions},annotations={self._annotations})'
+
+
+class OWLNaryAxiom(Generic[_C], OWLAxiom, metaclass=ABCMeta):
+    """Represents an axiom that contains two or more operands that could also be represented with multiple pairwise
+    axioms.
+
+    Args:
+        _C: Class of contained objects.
+    """
+    __slots__ = ()
+
+    @abstractmethod
+    def as_pairwise_axioms(self) -> Iterable['OWLNaryAxiom[_C]']:
+        pass
+
+
+# noinspection PyUnresolvedReferences
+# noinspection PyDunderSlots
+class OWLNaryClassAxiom(OWLClassAxiom, OWLNaryAxiom[OWLClassExpression], metaclass=ABCMeta):
+    """Represents an axiom that contains two or more operands that could also be represented with
+        multiple pairwise axioms."""
+    __slots__ = '_class_expressions'
+    _class_expressions: List[OWLClassExpression]
+
+    @abstractmethod
+    def __init__(self, class_expressions: List[OWLClassExpression],
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        self._class_expressions = [*class_expressions]
+        super().__init__(annotations=annotations)
+
+    def class_expressions(self) -> Iterable[OWLClassExpression]:
+        """Gets all of the top level class expressions that appear in this axiom.
+
+        Returns:
+            Sorted stream of class expressions that appear in the axiom.
+        """
+        yield from self._class_expressions
+
+    def as_pairwise_axioms(self) -> Iterable['OWLNaryClassAxiom']:
+        """Gets this axiom as a set of pairwise axioms; if the axiom contains only two operands,
+        the axiom itself is returned unchanged, including its annotations.
+
+        Returns:
+            This axiom as a set of pairwise axioms.
+        """
+        if len(self._class_expressions) < 3:
+            yield self
+        else:
+            yield from map(type(self), combinations(self._class_expressions, 2))
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._class_expressions == other._class_expressions and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((*self._class_expressions, *self._annotations))
+
+    def __repr__(self):
+        return f'{type(self).__name__}({self._class_expressions},{self._annotations})'
+
+
+class OWLEquivalentClassesAxiom(OWLNaryClassAxiom):
+    """Represents an EquivalentClasses axiom in the OWL 2 Specification."""
+    __slots__ = ()
+
+    def __init__(self, class_expressions: List[OWLClassExpression],
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(class_expressions=class_expressions, annotations=annotations)
+
+    def contains_named_equivalent_class(self) -> bool:
+        return any(isinstance(ce, OWLClass) for ce in self._class_expressions)
+
+    def contains_owl_nothing(self) -> bool:
+        return any(isinstance(ce, OWLNothing) for ce in self._class_expressions)
+
+    def contains_owl_thing(self) -> bool:
+        return any(isinstance(ce, OWLThing) for ce in self._class_expressions)
+
+    def named_classes(self) -> Iterable[OWLClass]:
+        yield from (ce for ce in self._class_expressions if isinstance(ce, OWLClass))
+
+
+class OWLDisjointClassesAxiom(OWLNaryClassAxiom):
+    """Represents a DisjointClasses axiom in the OWL 2 Specification."""
+    __slots__ = ()
+
+    def __init__(self, class_expressions: List[OWLClassExpression],
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(class_expressions=class_expressions, annotations=annotations)
+
+
+class OWLNaryIndividualAxiom(OWLIndividualAxiom, OWLNaryAxiom[OWLIndividual], metaclass=ABCMeta):
+    """Represents an axiom that contains two or more operands that could also be represented with
+            multiple pairwise individual axioms."""
+    __slots__ = '_individuals'
+
+    _individuals: List[OWLIndividual]
+
+    @abstractmethod
+    def __init__(self, individuals: List[OWLIndividual],
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        self._individuals = [*individuals]
+        super().__init__(annotations=annotations)
+
+    def individuals(self) -> Iterable[OWLIndividual]:
+        """Get the individuals.
+
+        Returns:
+            Generator containing the individuals.
+        """
+        yield from self._individuals
+
+    def as_pairwise_axioms(self) -> Iterable['OWLNaryIndividualAxiom']:
+        if len(self._individuals) < 3:
+            yield self
+        else:
+            yield from map(type(self), combinations(self._individuals, 2))
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._individuals == other._individuals and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((*self._individuals, *self._annotations))
+
+    def __repr__(self):
+        return f'{type(self).__name__}({self._individuals},{self._annotations})'
+
+
+class OWLDifferentIndividualsAxiom(OWLNaryIndividualAxiom):
+    """Represents a DifferentIndividuals axiom in the OWL 2 Specification."""
+    __slots__ = ()
+
+    def __init__(self, individuals: List[OWLIndividual],
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(individuals=individuals, annotations=annotations)
+
+
+class OWLSameIndividualAxiom(OWLNaryIndividualAxiom):
+    """Represents a SameIndividual axiom in the OWL 2 Specification."""
+    __slots__ = ()
+
+    def __init__(self, individuals: List[OWLIndividual],
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(individuals=individuals, annotations=annotations)
+
+
+class OWLNaryPropertyAxiom(Generic[_P], OWLPropertyAxiom, OWLNaryAxiom[_P], metaclass=ABCMeta):
+    """Represents an axiom that contains two or more operands that could also be represented with
+       multiple pairwise property axioms."""
+    __slots__ = '_properties'
+
+    _properties: List[_P]
+
+    @abstractmethod
+    def __init__(self, properties: List[_P], annotations: Optional[Iterable['OWLAnnotation']] = None):
+        self._properties = [*properties]
+        super().__init__(annotations=annotations)
+
+    def properties(self) -> Iterable[_P]:
+        """Get all the properties that appear in the axiom.
+
+        Returns:
+            Generator containing the properties.
+        """
+        yield from self._properties
+
+    def as_pairwise_axioms(self) -> Iterable['OWLNaryPropertyAxiom']:
+        if len(self._properties) < 3:
+            yield self
+        else:
+            yield from map(type(self), combinations(self._properties, 2))
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._properties == other._properties and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((*self._properties, *self._annotations))
+
+    def __repr__(self):
+        return f'{type(self).__name__}({self._properties},{self._annotations})'
+
+
+class OWLEquivalentObjectPropertiesAxiom(OWLNaryPropertyAxiom[OWLObjectPropertyExpression], OWLObjectPropertyAxiom):
+    """Represents EquivalentObjectProperties axioms in the OWL 2 Specification."""
+    __slots__ = ()
+
+    def __init__(self, properties: List[OWLObjectPropertyExpression],
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(properties=properties, annotations=annotations)
+
+
+class OWLDisjointObjectPropertiesAxiom(OWLNaryPropertyAxiom[OWLObjectPropertyExpression], OWLObjectPropertyAxiom):
+    """Represents DisjointObjectProperties axioms in the OWL 2 Specification."""
+    __slots__ = ()
+
+    def __init__(self, properties: List[OWLObjectPropertyExpression],
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(properties=properties, annotations=annotations)
+
+
+class OWLInverseObjectPropertiesAxiom(OWLNaryPropertyAxiom[OWLObjectPropertyExpression], OWLObjectPropertyAxiom):
+    """Represents InverseObjectProperties axioms in the OWL 2 Specification."""
+    __slots__ = '_first', '_second'
+
+    _first: OWLObjectPropertyExpression
+    _second: OWLObjectPropertyExpression
+
+    def __init__(self, first: OWLObjectPropertyExpression, second: OWLObjectPropertyExpression,
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        self._first = first
+        self._second = second
+        super().__init__(properties=[first, second], annotations=annotations)
+
+    def get_first_property(self) -> OWLObjectPropertyExpression:
+        return self._first
+
+    def get_second_property(self) -> OWLObjectPropertyExpression:
+        return self._second
+
+    def __repr__(self):
+        return f'OWLInverseObjectPropertiesAxiom(first={self._first},second={self._second},' \
+               f'annotations={self._annotations})'
+
+
+class OWLEquivalentDataPropertiesAxiom(OWLNaryPropertyAxiom[OWLDataPropertyExpression], OWLDataPropertyAxiom):
+    """Represents EquivalentDataProperties axioms in the OWL 2 Specification."""
+    __slots__ = ()
+
+    def __init__(self, properties: List[OWLDataPropertyExpression],
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(properties=properties, annotations=annotations)
+
+
+class OWLDisjointDataPropertiesAxiom(OWLNaryPropertyAxiom[OWLDataPropertyExpression], OWLDataPropertyAxiom):
+    """Represents DisjointDataProperties axioms in the OWL 2 Specification."""
+    __slots__ = ()
+
+    def __init__(self, properties: List[OWLDataPropertyExpression],
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(properties=properties, annotations=annotations)
+
+
+class OWLSubClassOfAxiom(OWLClassAxiom):
+    """Represents an SubClassOf axiom in the OWL 2 Specification."""
+    __slots__ = '_sub_class', '_super_class'
+
+    _sub_class: OWLClassExpression
+    _super_class: OWLClassExpression
+
+    def __init__(self, sub_class: OWLClassExpression, super_class: OWLClassExpression,
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        """Get an equivalent classes axiom with specified operands and no annotations.
+
+        Args:
+            sub_class: The sub-class.
+            super_class: The super class.
+            annotations: Annotations.
+        """
+        self._sub_class = sub_class
+        self._super_class = super_class
+        super().__init__(annotations=annotations)
+
+    def get_sub_class(self) -> OWLClassExpression:
+        return self._sub_class
+
+    def get_super_class(self) -> OWLClassExpression:
+        return self._super_class
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._super_class == other._super_class and self._sub_class == other._sub_class \
+                and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._super_class, self._sub_class, *self._annotations))
+
+    def __repr__(self):
+        return f'OWLSubClassOfAxiom(sub_class={self._sub_class},super_class={self._super_class},' \
+               f'annotations={self._annotations})'
+
+
+class OWLDisjointUnionAxiom(OWLClassAxiom):
+    """Represents a DisjointUnion axiom in the OWL 2 Specification."""
+    __slots__ = '_cls', '_class_expressions'
+
+    _cls: OWLClass
+    _class_expressions: List[OWLClassExpression]
+
+    def __init__(self, cls_: OWLClass, class_expressions: List[OWLClassExpression],
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        self._cls = cls_
+        self._class_expressions = class_expressions
+        super().__init__(annotations=annotations)
+
+    def get_owl_class(self) -> OWLClass:
+        return self._cls
+
+    def get_class_expressions(self) -> Iterable[OWLClassExpression]:
+        yield from self._class_expressions
+
+    def get_owl_equivalent_classes_axiom(self) -> OWLEquivalentClassesAxiom:
+        return OWLEquivalentClassesAxiom(self._cls, OWLObjectUnionOf(self._class_expressions))
+
+    def get_owl_disjoint_classes_axiom(self) -> OWLDisjointClassesAxiom:
+        return OWLDisjointClassesAxiom(self._class_expressions)
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._cls == other._cls and self._class_expressions == other._class_expressions \
+                and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._cls, *self._class_expressions, *self._annotations))
+
+    def __repr__(self):
+        return f'OWLDisjointUnionAxiom(class={self._cls},class_expressions={self._class_expressions},' \
+               f'annotations={self._annotations})'
+
+
+class OWLClassAssertionAxiom(OWLIndividualAxiom):
+    """Represents ClassAssertion axioms in the OWL 2 Specification."""
+    __slots__ = '_individual', '_class_expression'
+
+    _individual: OWLIndividual
+    _class_expression: OWLClassExpression
+
+    def __init__(self, individual: OWLIndividual, class_expression: OWLClassExpression,
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        """Get a ClassAssertion axiom for the specified individual and class expression.
+        Args:
+            individual: The individual.
+            class_expression: The class the individual belongs to.
+            annotations: Annotations.
+        """
+        self._individual = individual
+        self._class_expression = class_expression
+        super().__init__(annotations=annotations)
+
+    def get_individual(self) -> OWLIndividual:
+        return self._individual
+
+    def get_class_expression(self) -> OWLClassExpression:
+        return self._class_expression
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._class_expression == other._class_expression and self._individual == other._individual \
+                and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._individual, self._class_expression, *self._annotations))
+
+    def __repr__(self):
+        return f'OWLClassAssertionAxiom(individual={self._individual},class_expression={self._class_expression},' \
+               f'annotations={self._annotations})'
+class OWLAnnotationProperty(OWLProperty):
+    """Represents an AnnotationProperty in the OWL 2 specification."""
+    __slots__ = '_iri'
+
+    _iri: IRI
+
+    def __init__(self, iri: IRI):
+        """Get a new OWLAnnotationProperty object.
+
+        Args:
+            iri: New OWLAnnotationProperty IRI.
+        """
+        self._iri = iri
+
+    def get_iri(self) -> IRI:
+        # documented in parent
+        return self._iri
+
+class OWLAnnotation(OWLObject):
+    """Annotations are used in the various types of annotation axioms, which bind annotations to their subjects
+    (i.e. axioms or declarations)."""
+    __slots__ = '_property', '_value'
+
+    _property: OWLAnnotationProperty
+    _value: OWLAnnotationValue
+
+    def __init__(self, property: OWLAnnotationProperty, value: OWLAnnotationValue):
+        """Gets an annotation.
+
+        Args:
+            property: the annotation property.
+            value: The annotation value.
+        """
+        self._property = property
+        self._value = value
+
+    def get_property(self) -> OWLAnnotationProperty:
+        """Gets the property that this annotation acts along.
+
+        Returns:
+            The annotation property.
+        """
+        return self._property
+
+    def get_value(self) -> OWLAnnotationValue:
+        """Gets the annotation value. The type of value will depend upon the type of the annotation e.g. whether the
+        annotation is an OWLLiteral, an IRI or an OWLAnonymousIndividual.
+
+        Returns:
+            The annotation value.
+        """
+        return self._value
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._property == other._property and self._value == other._value
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._property, self._value))
+
+    def __repr__(self):
+        return f'OWLAnnotation({self._property}, {self._value})'
+class OWLAnnotationAxiom(OWLAxiom, metaclass=ABCMeta):
+    """A super interface for annotation axioms."""
+    __slots__ = ()
+
+    def is_annotation_axiom(self) -> bool:
+        return True
+class OWLAnnotationAssertionAxiom(OWLAnnotationAxiom):
+    """Represents AnnotationAssertion axioms in the OWL 2 specification."""
+    __slots__ = '_subject', '_annotation'
+
+    _subject: OWLAnnotationSubject
+    _annotation: OWLAnnotation
+
+    def __init__(self, subject: OWLAnnotationSubject, annotation: OWLAnnotation):
+        """Get an annotation assertion axiom - with annotations.
+
+        Args:
+            subject: Subject.
+            annotation: Annotation.
+        """
+        assert isinstance(subject, OWLAnnotationSubject)
+        assert isinstance(annotation, OWLAnnotation)
+
+        self._subject = subject
+        self._annotation = annotation
+
+    def get_subject(self) -> OWLAnnotationSubject:
+        """Gets the subject of this object.
+
+        Returns:
+            The subject.
+        """
+        return self._subject
+
+    def get_property(self) -> OWLAnnotationProperty:
+        """Gets the property.
+
+        Returns:
+            The property.
+        """
+        return self._annotation.get_property()
+
+    def get_value(self) -> OWLAnnotationValue:
+        """Gets the annotation value. This is either an IRI, an OWLAnonymousIndividual or an OWLLiteral.
+
+        Returns:
+            The annotation value.
+        """
+        return self._annotation.get_value()
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._subject == other._subject and self._annotation == other._annotation
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._subject, self._annotation))
+
+    def __repr__(self):
+        return f'OWLAnnotationAssertionAxiom({self._subject}, {self._annotation})'
+class OWLSubAnnotationPropertyOfAxiom(OWLAnnotationAxiom):
+    """Represents an SubAnnotationPropertyOf axiom in the OWL 2 specification."""
+    __slots__ = '_sub_property', '_super_property'
+
+    _sub_property: OWLAnnotationProperty
+    _super_property: OWLAnnotationProperty
+
+    def __init__(self, sub_property: OWLAnnotationProperty, super_property: OWLAnnotationProperty,
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        self._sub_property = sub_property
+        self._super_property = super_property
+        super().__init__(annotations=annotations)
+
+    def get_sub_property(self) -> OWLAnnotationProperty:
+        return self._sub_property
+
+    def get_super_property(self) -> OWLAnnotationProperty:
+        return self._super_property
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._sub_property == other._sub_property and self._super_property == other._super_property \
+                and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._sub_property, self._super_property, *self._annotations))
+
+    def __repr__(self):
+        return f'OWLSubAnnotationPropertyOfAxiom(sub_property={self._sub_property},' \
+               f'super_property={self._super_property},annotations={self._annotations})'
+class OWLAnnotationPropertyDomainAxiom(OWLAnnotationAxiom):
+    """Represents an AnnotationPropertyDomain axiom in the OWL 2 specification."""
+    __slots__ = '_property', '_domain'
+
+    _property: OWLAnnotationProperty
+    _domain: IRI
+
+    def __init__(self, property_: OWLAnnotationProperty, domain: IRI,
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        self._property = property_
+        self._domain = domain
+        super().__init__(annotations=annotations)
+
+    def get_property(self) -> OWLAnnotationProperty:
+        return self._property
+
+    def get_domain(self) -> IRI:
+        return self._domain
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._property == other._property and self._domain == other._domain \
+                and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._property, self._domain, *self._annotations))
+
+    def __repr__(self):
+        return f'OWLAnnotationPropertyDomainAxiom({repr(self._property)},{repr(self._domain)},' \
+               f'{repr(self._annotations)})'
+class OWLAnnotationPropertyRangeAxiom(OWLAnnotationAxiom):
+    """Represents an AnnotationPropertyRange axiom in the OWL 2 specification."""
+    __slots__ = '_property', '_range'
+
+    _property: OWLAnnotationProperty
+    _range: IRI
+
+    def __init__(self, property_: OWLAnnotationProperty, range_: IRI,
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        self._property = property_
+        self._range = range_
+        super().__init__(annotations=annotations)
+
+    def get_property(self) -> OWLAnnotationProperty:
+        return self._property
+
+    def get_range(self) -> IRI:
+        return self._range
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._property == other._property and self._range == other._range \
+                and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._property, self._range, *self._annotations))
+
+    def __repr__(self):
+        return f'OWLAnnotationPropertyRangeAxiom({repr(self._property)},{repr(self._range)},' \
+               f'{repr(self._annotations)})'
+class OWLSubPropertyAxiom(Generic[_P], OWLPropertyAxiom):
+    """
+    Base interface for object and data sub-property axioms.
+    """
+    __slots__ = '_sub_property', '_super_property'
+
+    _sub_property: _P
+    _super_property: _P
+
+    @abstractmethod
+    def __init__(self, sub_property: _P, super_property: _P,
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        self._sub_property = sub_property
+        self._super_property = super_property
+        super().__init__(annotations=annotations)
+
+    def get_sub_property(self) -> _P:
+        return self._sub_property
+
+    def get_super_property(self) -> _P:
+        return self._super_property
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._sub_property == other._sub_property and self._super_property == other._super_property \
+                and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._sub_property, self._super_property, *self._annotations))
+
+    def __repr__(self):
+        return f'{type(self).__name__}(sub_property={self._sub_property},super_property={self._super_property},' \
+               f'annotations={self._annotations})'
+class OWLSubObjectPropertyOfAxiom(OWLSubPropertyAxiom[OWLObjectPropertyExpression], OWLObjectPropertyAxiom):
+    """Represents a SubObjectPropertyOf axiom in the OWL 2 specification."""
+    __slots__ = ()
+
+    def __init__(self, sub_property: OWLObjectPropertyExpression, super_property: OWLObjectPropertyExpression,
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(sub_property=sub_property, super_property=super_property, annotations=annotations)
+class OWLSubDataPropertyOfAxiom(OWLSubPropertyAxiom[OWLDataPropertyExpression], OWLDataPropertyAxiom):
+    """Represents a SubDataPropertyOf axiom in the OWL 2 specification."""
+    __slots__ = ()
+
+    def __init__(self, sub_property: OWLDataPropertyExpression, super_property: OWLDataPropertyExpression,
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(sub_property=sub_property, super_property=super_property, annotations=annotations)
+
+class OWLPropertyAssertionAxiom(Generic[_P, _C], OWLIndividualAxiom, metaclass=ABCMeta):
+    """Represents a PropertyAssertion axiom in the OWL 2 specification."""
+    __slots__ = '_subject', '_property', '_object'
+
+    _subject: OWLIndividual
+    _property: _P
+    _object: _C
+
+    @abstractmethod
+    def __init__(self, subject: OWLIndividual, property_: _P, object_: _C,
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        """Get a PropertyAssertion axiom for the specified subject, property, object.
+        Args:
+            subject: The subject of the property assertion.
+            property_: The property of the property assertion.
+            object_: The object of the property assertion.
+            annotations: Annotations.
+        """
+        assert isinstance(subject, OWLIndividual)
+
+        self._subject = subject
+        self._property = property_
+        self._object = object_
+        super().__init__(annotations=annotations)
+
+    def get_subject(self) -> OWLIndividual:
+        return self._subject
+
+    def get_property(self) -> _P:
+        return self._property
+
+    def get_object(self) -> _C:
+        return self._object
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._subject == other._subject and self._property == other._property and \
+                self._object == other._object and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._subject, self._property, self._object, *self._annotations))
+
+    def __repr__(self):
+        return f'{type(self).__name__}(subject={self._subject},property={self._property},' \
+               f'object={self._object},annotation={self._annotations})'
+class OWLObjectPropertyAssertionAxiom(OWLPropertyAssertionAxiom[OWLObjectPropertyExpression, OWLIndividual]):
+    """Represents an ObjectPropertyAssertion axiom in the OWL 2 specification."""
+    __slots__ = ()
+
+    def __init__(self, subject: OWLIndividual, property_: OWLObjectPropertyExpression, object_: OWLIndividual,
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(subject, property_, object_, annotations)
+class OWLNegativeObjectPropertyAssertionAxiom(OWLPropertyAssertionAxiom[OWLObjectPropertyExpression, OWLIndividual]):
+    """Represents a NegativeObjectPropertyAssertion axiom in the OWL 2 specification."""
+    __slots__ = ()
+
+    def __init__(self, subject: OWLIndividual, property_: OWLObjectPropertyExpression, object_: OWLIndividual,
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(subject, property_, object_, annotations)
+class OWLDataPropertyAssertionAxiom(OWLPropertyAssertionAxiom[OWLDataPropertyExpression, OWLLiteral]):
+    """Represents an DataPropertyAssertion axiom in the OWL 2 specification."""
+    __slots__ = ()
+
+    def __init__(self, subject: OWLIndividual, property_: OWLDataPropertyExpression, object_: OWLLiteral,
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(subject, property_, object_, annotations)
+class OWLNegativeDataPropertyAssertionAxiom(OWLPropertyAssertionAxiom[OWLDataPropertyExpression, OWLLiteral]):
+    """Represents an NegativeDataPropertyAssertion axiom in the OWL 2 specification."""
+    __slots__ = ()
+
+    def __init__(self, subject: OWLIndividual, property_: OWLDataPropertyExpression, object_: OWLLiteral,
+                 annotations: Optional[Iterable['OWLAnnotation']] = None):
+        super().__init__(subject, property_, object_, annotations)
+class OWLUnaryPropertyAxiom(Generic[_P], OWLPropertyAxiom, metaclass=ABCMeta):
+    """Unary property axiom."""
+    __slots__ = '_property'
+
+    _property: _P
+
+    def __init__(self, property_: _P, annotations: Optional[Iterable[OWLAnnotation]] = None):
+        self._property = property_
+        super().__init__(annotations=annotations)
+
+    def get_property(self) -> _P:
+        return self._property
+
+
+class OWLObjectPropertyCharacteristicAxiom(OWLUnaryPropertyAxiom[OWLObjectPropertyExpression],
+                                           OWLObjectPropertyAxiom, metaclass=ABCMeta):
+    """Base interface for functional object property axiom."""
+    __slots__ = ()
+
+    @abstractmethod
+    def __init__(self, property_: OWLObjectPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
+        super().__init__(property_=property_, annotations=annotations)
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._property == other._property and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._property, *self._annotations))
+
+    def __repr__(self):
+        return f"{type(self).__name__}({repr(self._property)},{repr(self._annotations)})"
+
+
+class OWLFunctionalObjectPropertyAxiom(OWLObjectPropertyCharacteristicAxiom):
+    """Represents FunctionalObjectProperty axioms in the OWL 2 specification."""
+    __slots__ = ()
+
+    def __init__(self, property_: OWLObjectPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
+        super().__init__(property_=property_, annotations=annotations)
+
+
+class OWLAsymmetricObjectPropertyAxiom(OWLObjectPropertyCharacteristicAxiom):
+    """Represents AsymmetricObjectProperty axioms in the OWL 2 specification."""
+    __slots__ = ()
+
+    def __init__(self, property_: OWLObjectPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
+        super().__init__(property_=property_, annotations=annotations)
+
+
+class OWLInverseFunctionalObjectPropertyAxiom(OWLObjectPropertyCharacteristicAxiom):
+    """Represents InverseFunctionalObjectProperty axioms in the OWL 2 specification."""
+    __slots__ = ()
+
+    def __init__(self, property_: OWLObjectPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
+        super().__init__(property_=property_, annotations=annotations)
+
+
+class OWLIrreflexiveObjectPropertyAxiom(OWLObjectPropertyCharacteristicAxiom):
+    """Represents IrreflexiveObjectProperty axioms in the OWL 2 specification."""
+    __slots__ = ()
+
+    def __init__(self, property_: OWLObjectPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
+        super().__init__(property_=property_, annotations=annotations)
+
+
+class OWLReflexiveObjectPropertyAxiom(OWLObjectPropertyCharacteristicAxiom):
+    """Represents ReflexiveObjectProperty axioms in the OWL 2 specification."""
+    __slots__ = ()
+
+    def __init__(self, property_: OWLObjectPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
+        super().__init__(property_=property_, annotations=annotations)
+
+
+class OWLSymmetricObjectPropertyAxiom(OWLObjectPropertyCharacteristicAxiom):
+    """Represents SymmetricObjectProperty axioms in the OWL 2 specification."""
+    __slots__ = ()
+
+    def __init__(self, property_: OWLObjectPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
+        super().__init__(property_=property_, annotations=annotations)
+
+
+class OWLTransitiveObjectPropertyAxiom(OWLObjectPropertyCharacteristicAxiom):
+    """Represents TransitiveObjectProperty axioms in the OWL 2 specification."""
+    __slots__ = ()
+
+    def __init__(self, property_: OWLObjectPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
+        super().__init__(property_=property_, annotations=annotations)
+
+
+class OWLDataPropertyCharacteristicAxiom(OWLUnaryPropertyAxiom[OWLDataPropertyExpression],
+                                         OWLDataPropertyAxiom, metaclass=ABCMeta):
+    """Base interface for Functional data property axiom."""
+    __slots__ = ()
+
+    @abstractmethod
+    def __init__(self, property_: OWLDataPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
+        super().__init__(property_=property_, annotations=annotations)
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._property == other._property and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._property, *self._annotations))
+
+    def __repr__(self):
+        return f"{type(self).__name__}({repr(self._property)},{repr(self._annotations)})"
+
+
+class OWLFunctionalDataPropertyAxiom(OWLDataPropertyCharacteristicAxiom):
+    """Represents FunctionalDataProperty axioms in the OWL 2 specification."""
+    __slots__ = ()
+
+    def __init__(self, property_: OWLDataPropertyExpression, annotations: Optional[Iterable[OWLAnnotation]] = None):
+        super().__init__(property_=property_, annotations=annotations)
+
+
+class OWLPropertyDomainAxiom(Generic[_P], OWLUnaryPropertyAxiom[_P], metaclass=ABCMeta):
+    """Represents ObjectPropertyDomain axioms in the OWL 2 specification."""
+    __slots__ = '_domain'
+
+    _domain: OWLClassExpression
+
+    @abstractmethod
+    def __init__(self, property_: _P, domain: OWLClassExpression,
+                 annotations: Optional[Iterable[OWLAnnotation]] = None):
+        self._domain = domain
+        super().__init__(property_=property_, annotations=annotations)
+
+    def get_domain(self) -> OWLClassExpression:
+        return self._domain
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._property == other._property and self._domain == other._domain \
+                and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._property, self._domain, *self._annotations))
+
+    def __repr__(self):
+        return f"{type(self).__name__}({repr(self._property)},{repr(self._domain)},{repr(self._annotations)})"
+
+
+class OWLPropertyRangeAxiom(Generic[_P, _R], OWLUnaryPropertyAxiom[_P], metaclass=ABCMeta):
+    """Represents ObjectPropertyRange axioms in the OWL 2 specification."""
+    __slots__ = '_range'
+
+    _range: _R
+
+    @abstractmethod
+    def __init__(self, property_: _P, range_: _R, annotations: Optional[Iterable[OWLAnnotation]] = None):
+        self._range = range_
+        super().__init__(property_=property_, annotations=annotations)
+
+    def get_range(self) -> _R:
+        return self._range
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._property == other._property and self._range == other._range \
+                and self._annotations == other._annotations
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._property, self._range, *self._annotations))
+
+    def __repr__(self):
+        return f"{type(self).__name__}({repr(self._property)},{repr(self._range)},{repr(self._annotations)})"
+
+
+class OWLObjectPropertyDomainAxiom(OWLPropertyDomainAxiom[OWLObjectPropertyExpression]):
+    """ Represents a ObjectPropertyDomain axiom in the OWL 2 Specification."""
+    __slots__ = ()
+
+    def __init__(self, property_: OWLObjectPropertyExpression, domain: OWLClassExpression,
+                 annotations: Optional[Iterable[OWLAnnotation]] = None):
+        super().__init__(property_=property_, domain=domain, annotations=annotations)
+
+
+class OWLDataPropertyDomainAxiom(OWLPropertyDomainAxiom[OWLDataPropertyExpression]):
+    """ Represents a DataPropertyDomain axiom in the OWL 2 Specification."""
+    __slots__ = ()
+
+    def __init__(self, property_: OWLDataPropertyExpression, domain: OWLClassExpression,
+                 annotations: Optional[Iterable[OWLAnnotation]] = None):
+        super().__init__(property_=property_, domain=domain, annotations=annotations)
+
+
+class OWLObjectPropertyRangeAxiom(OWLPropertyRangeAxiom[OWLObjectPropertyExpression, OWLClassExpression]):
+    """ Represents a ObjectPropertyRange axiom in the OWL 2 Specification."""
+    __slots__ = ()
+
+    def __init__(self, property_: OWLObjectPropertyExpression, range_: OWLClassExpression,
+                 annotations: Optional[Iterable[OWLAnnotation]] = None):
+        super().__init__(property_=property_, range_=range_, annotations=annotations)
+
+
+class OWLDataPropertyRangeAxiom(OWLPropertyRangeAxiom[OWLDataPropertyExpression, OWLDataRange]):
+    """ Represents a DataPropertyRange axiom in the OWL 2 Specification."""
+    __slots__ = ()
+
+    def __init__(self, property_: OWLDataPropertyExpression, range_: OWLDataRange,
+                 annotations: Optional[Iterable[OWLAnnotation]] = None):
+        super().__init__(property_=property_, range_=range_, annotations=annotations)
diff --git a/owlapy/owl_individual.py b/owlapy/owl_individual.py
new file mode 100644
index 00000000..4d9ade9d
--- /dev/null
+++ b/owlapy/owl_individual.py
@@ -0,0 +1,42 @@
+from abc import ABCMeta
+from .owlobject import OWLObject, OWLEntity
+from .iri import IRI
+from typing import Final, Union
+class OWLIndividual(OWLObject, metaclass=ABCMeta):
+    """Represents a named or anonymous individual."""
+    __slots__ = ()
+    pass
+
+class OWLNamedIndividual(OWLIndividual, OWLEntity):
+    """Represents a Named Individual in the OWL 2 Specification."""
+    __slots__ = '_iri'
+    type_index: Final = 1005
+
+    _iri: IRI
+
+    def __init__(self, iri: Union[IRI, str]):
+        """Gets an instance of OWLNamedIndividual that has the specified IRI.
+
+        Args:
+            iri: an instance of IRI Class or a string representing the iri
+
+        Returns:
+            An OWLNamedIndividual that has the specified IRI.
+        """
+        if isinstance(iri, IRI):
+            self._iri = iri
+        else:
+            self._iri = IRI.create(iri)
+
+    def get_iri(self) -> IRI:
+        # TODO:CD: can be deprecated
+        # documented in parent
+        return self._iri
+
+    @property
+    def iri(self):
+        return self._iri
+
+    @property
+    def str(self):
+        return self._iri.as_str()
diff --git a/owlapy/owl_literal.py b/owlapy/owl_literal.py
new file mode 100644
index 00000000..fac9a516
--- /dev/null
+++ b/owlapy/owl_literal.py
@@ -0,0 +1,503 @@
+from abc import ABCMeta, abstractmethod
+from functools import total_ordering
+from .owl_annotation import OWLAnnotationValue
+from typing import Final, Optional, Union, TypeVar, Set
+from .types import OWLDatatype
+from datetime import datetime, date
+from pandas import Timedelta
+from owlapy.vocab import OWLRDFVocabulary, XSDVocabulary
+from .owl_property import OWLObjectProperty, OWLDataProperty
+
+Literals = Union['OWLLiteral', int, float, bool, Timedelta, datetime, date, str]  #:
+_R = TypeVar('_R', bound='OWLPropertyRange')  #:
+
+class OWLLiteral(OWLAnnotationValue, metaclass=ABCMeta):
+    """Represents a Literal in the OWL 2 Specification."""
+    __slots__ = ()
+
+    type_index: Final = 4008
+
+    def __new__(cls, value, type_: Optional[OWLDatatype] = None):
+        """Convenience method that obtains a literal.
+
+        Args:
+            value: The value of the literal.
+            type_: The datatype of the literal.
+        """
+        if type_ is not None:
+            if type_ == BooleanOWLDatatype:
+                return super().__new__(_OWLLiteralImplBoolean)
+            elif type_ == IntegerOWLDatatype:
+                return super().__new__(_OWLLiteralImplInteger)
+            elif type_ == DoubleOWLDatatype:
+                return super().__new__(_OWLLiteralImplDouble)
+            elif type_ == StringOWLDatatype:
+                return super().__new__(_OWLLiteralImplString)
+            elif type_ == DateOWLDatatype:
+                return super().__new__(_OWLLiteralImplDate)
+            elif type_ == DateTimeOWLDatatype:
+                return super().__new__(_OWLLiteralImplDateTime)
+            elif type_ == DurationOWLDatatype:
+                return super().__new__(_OWLLiteralImplDuration)
+            else:
+                return super().__new__(_OWLLiteralImpl)
+        if isinstance(value, bool):
+            return super().__new__(_OWLLiteralImplBoolean)
+        elif isinstance(value, int):
+            return super().__new__(_OWLLiteralImplInteger)
+        elif isinstance(value, float):
+            return super().__new__(_OWLLiteralImplDouble)
+        elif isinstance(value, str):
+            return super().__new__(_OWLLiteralImplString)
+        elif isinstance(value, datetime):
+            return super().__new__(_OWLLiteralImplDateTime)
+        elif isinstance(value, date):
+            return super().__new__(_OWLLiteralImplDate)
+        elif isinstance(value, Timedelta):
+            return super().__new__(_OWLLiteralImplDuration)
+        # TODO XXX
+        raise NotImplementedError(value)
+
+    def get_literal(self) -> str:
+        """Gets the lexical value of this literal. Note that the language tag is not included.
+
+        Returns:
+            The lexical value of this literal.
+        """
+        return str(self._v)
+
+    def is_boolean(self) -> bool:
+        """Whether this literal is typed as boolean."""
+        return False
+
+    def parse_boolean(self) -> bool:
+        """Parses the lexical value of this literal into a bool. The lexical value of this literal should be in the
+        lexical space of the boolean datatype ("http://www.w3.org/2001/XMLSchema#boolean").
+
+        Returns:
+            A bool value that is represented by this literal.
+        """
+        raise ValueError
+
+    def is_double(self) -> bool:
+        """Whether this literal is typed as double."""
+        return False
+
+    def parse_double(self) -> float:
+        """Parses the lexical value of this literal into a double. The lexical value of this literal should be in the
+        lexical space of the double datatype ("http://www.w3.org/2001/XMLSchema#double").
+
+        Returns:
+            A double value that is represented by this literal.
+        """
+        raise ValueError
+
+    def is_integer(self) -> bool:
+        """Whether this literal is typed as integer."""
+        return False
+
+    def parse_integer(self) -> int:
+        """Parses the lexical value of this literal into an integer. The lexical value of this literal should be in the
+        lexical space of the integer datatype ("http://www.w3.org/2001/XMLSchema#integer").
+
+        Returns:
+            An integer value that is represented by this literal.
+        """
+        raise ValueError
+
+    def is_string(self) -> bool:
+        """Whether this literal is typed as string."""
+        return False
+
+    def parse_string(self) -> str:
+        """Parses the lexical value of this literal into a string. The lexical value of this literal should be in the
+        lexical space of the string datatype ("http://www.w3.org/2001/XMLSchema#string").
+
+        Returns:
+            A string value that is represented by this literal.
+        """
+        raise ValueError
+
+    def is_date(self) -> bool:
+        """Whether this literal is typed as date."""
+        return False
+
+    def parse_date(self) -> date:
+        """Parses the lexical value of this literal into a date. The lexical value of this literal should be in the
+        lexical space of the date datatype ("http://www.w3.org/2001/XMLSchema#date").
+
+        Returns:
+            A date value that is represented by this literal.
+        """
+        raise ValueError
+
+    def is_datetime(self) -> bool:
+        """Whether this literal is typed as dateTime."""
+        return False
+
+    def parse_datetime(self) -> datetime:
+        """Parses the lexical value of this literal into a datetime. The lexical value of this literal should be in the
+        lexical space of the dateTime datatype ("http://www.w3.org/2001/XMLSchema#dateTime").
+
+        Returns:
+            A datetime value that is represented by this literal.
+        """
+        raise ValueError
+
+    def is_duration(self) -> bool:
+        """Whether this literal is typed as duration."""
+        return False
+
+    def parse_duration(self) -> Timedelta:
+        """Parses the lexical value of this literal into a Timedelta. The lexical value of this literal should be in the
+        lexical space of the duration datatype ("http://www.w3.org/2001/XMLSchema#duration").
+
+        Returns:
+            A Timedelta value that is represented by this literal.
+        """
+        raise ValueError
+
+    # noinspection PyMethodMayBeStatic
+    def is_literal(self) -> bool:
+        # documented in parent
+        return True
+
+    def as_literal(self) -> 'OWLLiteral':
+        # documented in parent
+        return self
+
+    def to_python(self) -> Literals:
+        return self._v
+
+    @abstractmethod
+    def get_datatype(self) -> OWLDatatype:
+        """Gets the OWLDatatype which types this literal.
+
+        Returns:
+            The OWLDatatype that types this literal.
+        """
+        pass
+@total_ordering
+class _OWLLiteralImplDouble(OWLLiteral):
+    __slots__ = '_v'
+
+    _v: float
+
+    def __init__(self, value, type_=None):
+        assert type_ is None or type_ == DoubleOWLDatatype
+        if not isinstance(value, float):
+            value = float(value)
+        self._v = value
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._v == other._v
+        return NotImplemented
+
+    def __lt__(self, other):
+        if type(other) is type(self):
+            return self._v < other._v
+        return NotImplemented
+
+    def __hash__(self):
+        return hash(self._v)
+
+    def __repr__(self):
+        return f'OWLLiteral({self._v})'
+
+    def is_double(self) -> bool:
+        return True
+
+    def parse_double(self) -> float:
+        # documented in parent
+        return self._v
+
+    # noinspection PyMethodMayBeStatic
+    def get_datatype(self) -> OWLDatatype:
+        # documented in parent
+        return DoubleOWLDatatype
+@total_ordering
+class _OWLLiteralImplInteger(OWLLiteral):
+    __slots__ = '_v'
+
+    _v: int
+
+    def __init__(self, value, type_=None):
+        assert type_ is None or type_ == IntegerOWLDatatype
+        if not isinstance(value, int):
+            value = int(value)
+        self._v = value
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._v == other._v
+        return NotImplemented
+
+    def __lt__(self, other):
+        if type(other) is type(self):
+            return self._v < other._v
+        return NotImplemented
+
+    def __hash__(self):
+        return hash(self._v)
+
+    def __repr__(self):
+        return f'OWLLiteral({self._v})'
+
+    def is_integer(self) -> bool:
+        return True
+
+    def parse_integer(self) -> int:
+        # documented in parent
+        return self._v
+
+    # noinspection PyMethodMayBeStatic
+    def get_datatype(self) -> OWLDatatype:
+        # documented in parent
+        return IntegerOWLDatatype
+class _OWLLiteralImplBoolean(OWLLiteral):
+    __slots__ = '_v'
+
+    _v: bool
+
+    def __init__(self, value, type_=None):
+        assert type_ is None or type_ == BooleanOWLDatatype
+        if not isinstance(value, bool):
+            from distutils.util import strtobool
+            value = bool(strtobool(value))
+        self._v = value
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._v == other._v
+        return NotImplemented
+
+    def __hash__(self):
+        return hash(self._v)
+
+    def __repr__(self):
+        return f'OWLLiteral({self._v})'
+
+    def is_boolean(self) -> bool:
+        return True
+
+    def parse_boolean(self) -> bool:
+        # documented in parent
+        return self._v
+
+    # noinspection PyMethodMayBeStatic
+    def get_datatype(self) -> OWLDatatype:
+        # documented in parent
+        return BooleanOWLDatatype
+@total_ordering
+class _OWLLiteralImplString(OWLLiteral):
+    __slots__ = '_v'
+
+    _v: str
+
+    def __init__(self, value, type_=None):
+        assert type_ is None or type_ == StringOWLDatatype
+        if not isinstance(value, str):
+            value = str(value)
+        self._v = value
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._v == other._v
+        return NotImplemented
+
+    def __lt__(self, other):
+        if type(other) is type(self):
+            return self._v < other._v
+        return NotImplemented
+
+    def __len__(self):
+        return len(self._v)
+
+    def __hash__(self):
+        return hash(self._v)
+
+    def __repr__(self):
+        return f'OWLLiteral({self._v})'
+
+    def is_string(self) -> bool:
+        return True
+
+    def parse_string(self) -> str:
+        # documented in parent
+        return self._v
+
+    # noinspection PyMethodMayBeStatic
+    def get_datatype(self) -> OWLDatatype:
+        # documented in parent
+        return StringOWLDatatype
+@total_ordering
+class _OWLLiteralImplDate(OWLLiteral):
+    __slots__ = '_v'
+
+    _v: date
+
+    def __init__(self, value, type_=None):
+        assert type_ is None or type_ == DateOWLDatatype
+        if not isinstance(value, date):
+            value = date.fromisoformat(value)
+        self._v = value
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._v == other._v
+        return NotImplemented
+
+    def __lt__(self, other):
+        if type(other) is type(self):
+            return self._v < other._v
+        return NotImplemented
+
+    def __hash__(self):
+        return hash(self._v)
+
+    def __repr__(self):
+        return f'OWLLiteral({self._v})'
+
+    def is_date(self) -> bool:
+        return True
+
+    def parse_date(self) -> date:
+        # documented in parent
+        return self._v
+
+    # noinspection PyMethodMayBeStatic
+    def get_datatype(self) -> OWLDatatype:
+        # documented in parent
+        return DateOWLDatatype
+
+
+@total_ordering
+class _OWLLiteralImplDateTime(OWLLiteral):
+    __slots__ = '_v'
+
+    _v: datetime
+
+    def __init__(self, value, type_=None):
+        assert type_ is None or type_ == DateTimeOWLDatatype
+        if not isinstance(value, datetime):
+            value = value.replace("Z", "+00:00") if isinstance(value, str) and value[-1] == "Z" else value
+            value = datetime.fromisoformat(value)
+        self._v = value
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._v == other._v
+        return NotImplemented
+
+    def __lt__(self, other):
+        if type(other) is type(self):
+            return self._v < other._v
+        return NotImplemented
+
+    def __hash__(self):
+        return hash(self._v)
+
+    def __repr__(self):
+        return f'OWLLiteral({self._v})'
+
+    def is_datetime(self) -> bool:
+        return True
+
+    def parse_datetime(self) -> datetime:
+        # documented in parent
+        return self._v
+
+    # noinspection PyMethodMayBeStatic
+    def get_datatype(self) -> OWLDatatype:
+        # documented in parent
+        return DateTimeOWLDatatype
+
+
+@total_ordering
+class _OWLLiteralImplDuration(OWLLiteral):
+    __slots__ = '_v'
+
+    _v: Timedelta
+
+    def __init__(self, value, type_=None):
+        assert type_ is None or type_ == DurationOWLDatatype
+        if not isinstance(value, Timedelta):
+            value = Timedelta(value)
+        self._v = value
+
+    def get_literal(self) -> str:
+        return self._v.isoformat()
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._v == other._v
+        return NotImplemented
+
+    def __lt__(self, other):
+        if type(other) is type(self):
+            return self._v < other._v
+        return NotImplemented
+
+    def __hash__(self):
+        return hash(self._v)
+
+    def __repr__(self):
+        return f'OWLLiteral({self._v})'
+
+    def is_duration(self) -> bool:
+        return True
+
+    def parse_duration(self) -> Timedelta:
+        # documented in parent
+        return self._v
+
+    # noinspection PyMethodMayBeStatic
+    def get_datatype(self) -> OWLDatatype:
+        # documented in parent
+        return DurationOWLDatatype
+
+
+class _OWLLiteralImpl(OWLLiteral):
+    __slots__ = '_v', '_datatype'
+
+    def __init__(self, v, type_: OWLDatatype):
+        assert isinstance(type_, OWLDatatype)
+        self._v = v
+        self._datatype = type_
+
+    def get_datatype(self) -> OWLDatatype:
+        return self._datatype
+
+    def __eq__(self, other):
+        if type(other) is type(self) and other.get_datatype() == self.get_datatype():
+            return self._v == other._v
+        return NotImplemented
+
+    def __hash__(self):
+        return hash((self._v, self._datatype))
+
+    def __repr__(self):
+        return f'OWLLiteral({repr(self._v)}, {self._datatype})'
+
+#: the built in top object property
+OWLTopObjectProperty: Final = OWLObjectProperty(OWLRDFVocabulary.OWL_TOP_OBJECT_PROPERTY.get_iri())
+#: the built in bottom object property
+OWLBottomObjectProperty: Final = OWLObjectProperty(OWLRDFVocabulary.OWL_BOTTOM_OBJECT_PROPERTY.get_iri())
+#: the built in top data property
+OWLTopDataProperty: Final = OWLDataProperty(OWLRDFVocabulary.OWL_TOP_DATA_PROPERTY.get_iri())
+#: the built in bottom data property
+OWLBottomDataProperty: Final = OWLDataProperty(OWLRDFVocabulary.OWL_BOTTOM_DATA_PROPERTY.get_iri())
+
+DoubleOWLDatatype: Final = OWLDatatype(XSDVocabulary.DOUBLE)  #: An object representing a double datatype.
+IntegerOWLDatatype: Final = OWLDatatype(XSDVocabulary.INTEGER)  #: An object representing an integer datatype.
+BooleanOWLDatatype: Final = OWLDatatype(XSDVocabulary.BOOLEAN)  #: An object representing the boolean datatype.
+StringOWLDatatype: Final = OWLDatatype(XSDVocabulary.STRING)  #: An object representing the string datatype.
+DateOWLDatatype: Final = OWLDatatype(XSDVocabulary.DATE)  #: An object representing the date datatype.
+DateTimeOWLDatatype: Final = OWLDatatype(XSDVocabulary.DATE_TIME)  #: An object representing the dateTime datatype.
+DurationOWLDatatype: Final = OWLDatatype(XSDVocabulary.DURATION)  #: An object representing the duration datatype.
+#: The OWL Datatype corresponding to the top data type
+TopOWLDatatype: Final = OWLDatatype(OWLRDFVocabulary.RDFS_LITERAL)
+
+NUMERIC_DATATYPES: Final[Set[OWLDatatype]] = {DoubleOWLDatatype, IntegerOWLDatatype}
+TIME_DATATYPES: Final[Set[OWLDatatype]] = {DateOWLDatatype, DateTimeOWLDatatype, DurationOWLDatatype}
diff --git a/owlapy/owl_property.py b/owlapy/owl_property.py
new file mode 100644
index 00000000..5a89984a
--- /dev/null
+++ b/owlapy/owl_property.py
@@ -0,0 +1,188 @@
+from .owlobject import OWLObject, OWLEntity
+from abc import ABCMeta, abstractmethod
+from typing import Final, Union
+from .iri import IRI
+
+class OWLPropertyExpression(OWLObject, metaclass=ABCMeta):
+    """Represents a property or possibly the inverse of a property."""
+    __slots__ = ()
+
+    def is_data_property_expression(self) -> bool:
+        """
+        Returns:
+            True if this is a data property.
+        """
+        return False
+
+    def is_object_property_expression(self) -> bool:
+        """
+        Returns:
+            True if this is an object property.
+        """
+        return False
+
+    def is_owl_top_object_property(self) -> bool:
+        """Determines if this is the owl:topObjectProperty.
+
+        Returns:
+            True if this property is the owl:topObjectProperty.
+        """
+        return False
+
+    def is_owl_top_data_property(self) -> bool:
+        """Determines if this is the owl:topDataProperty.
+
+        Returns:
+            True if this property is the owl:topDataProperty.
+        """
+        return False
+class OWLObjectPropertyExpression(OWLPropertyExpression):
+    """A high level interface to describe different types of object properties."""
+    __slots__ = ()
+
+    @abstractmethod
+    def get_inverse_property(self) -> 'OWLObjectPropertyExpression':
+        """Obtains the property that corresponds to the inverse of this property.
+
+        Returns:
+            The inverse of this property. Note that this property will not necessarily be in the simplest form.
+        """
+        pass
+
+    @abstractmethod
+    def get_named_property(self) -> 'OWLObjectProperty':
+        """Get the named object property used in this property expression.
+
+        Returns:
+            P if this expression is either inv(P) or P.
+        """
+        pass
+
+    def is_object_property_expression(self) -> bool:
+        # documented in parent
+        return True
+class OWLDataPropertyExpression(OWLPropertyExpression, metaclass=ABCMeta):
+    """A high level interface to describe different types of data properties."""
+    __slots__ = ()
+
+    def is_data_property_expression(self):
+        # documented in parent
+        return True
+
+class OWLProperty(OWLPropertyExpression, OWLEntity, metaclass=ABCMeta):
+    """A marker interface for properties that aren't expression i.e. named properties. By definition, properties
+    are either data properties or object properties."""
+    __slots__ = ()
+    pass
+
+class OWLObjectProperty(OWLObjectPropertyExpression, OWLProperty):
+    """Represents an Object Property in the OWL 2 Specification."""
+    __slots__ = '_iri'
+    type_index: Final = 1002
+
+    _iri: Union['IRI', str]
+
+    def get_named_property(self) -> 'OWLObjectProperty':
+        # documented in parent
+        return self
+
+    def __init__(self, iri: Union['IRI', str]):
+        """Gets an instance of OWLObjectProperty that has the specified IRI.
+
+        Args:
+            iri: The IRI.
+        """
+        if isinstance(iri, IRI):
+            self._iri = iri
+        else:
+            self._iri = IRI.create(iri)
+
+    def get_inverse_property(self) -> 'OWLObjectInverseOf':
+        # documented in parent
+        return OWLObjectInverseOf(self)
+
+    @property
+    def str(self) -> str:
+        return self._iri.as_str()
+
+    @property
+    def iri(self) -> str:
+        return self._iri
+
+    def get_iri(self) -> 'IRI':
+        # TODO:CD: can be deprecated
+        # documented in parent
+        return self._iri
+
+    def is_owl_top_object_property(self) -> bool:
+        # documented in parent
+        return self.get_iri() == OWLRDFVocabulary.OWL_TOP_OBJECT_PROPERTY.get_iri()
+
+class OWLObjectInverseOf(OWLObjectPropertyExpression):
+    """Represents the inverse of a property expression (ObjectInverseOf). This can be used to refer to the inverse of
+    a property, without actually naming the property. For example, consider the property hasPart, the inverse property
+    of hasPart (isPartOf) can be referred to using this interface inverseOf(hasPart), which can be used in
+    restrictions e.g. inverseOf(hasPart) some Car refers to the set of things that are part of at least one car."""
+    __slots__ = '_inverse_property'
+    type_index: Final = 1003
+
+    _inverse_property: OWLObjectProperty
+
+    def __init__(self, property: OWLObjectProperty):
+        """Gets the inverse of an object property.
+
+        Args:
+            property: The property of which the inverse will be returned.
+        """
+        self._inverse_property = property
+
+    def get_inverse(self) -> OWLObjectProperty:
+        """Gets the property expression that this is the inverse of.
+
+        Returns:
+            The object property expression such that this object property expression is an inverse of it.
+        """
+        return self._inverse_property
+
+    def get_inverse_property(self) -> OWLObjectProperty:
+        # documented in parent
+        return self.get_inverse()
+
+    def get_named_property(self) -> OWLObjectProperty:
+        # documented in parent
+        return self._inverse_property
+
+    def __repr__(self):
+        return f"OWLObjectInverseOf({repr(self._inverse_property)})"
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._inverse_property == other._inverse_property
+        return NotImplemented
+
+    def __hash__(self):
+        return hash(self._inverse_property)
+
+
+class OWLDataProperty(OWLDataPropertyExpression, OWLProperty):
+    """Represents a Data Property in the OWL 2 Specification."""
+    __slots__ = '_iri'
+    type_index: Final = 1004
+
+    _iri: 'IRI'
+
+    def __init__(self, iri: 'IRI'):
+        """Gets an instance of OWLDataProperty that has the specified IRI.
+
+        Args:
+            iri: The IRI.
+        """
+        self._iri = iri
+
+    def get_iri(self) -> 'IRI':
+        # documented in parent
+        return self._iri
+
+    def is_owl_top_data_property(self) -> bool:
+        # documented in parent
+        return self.get_iri() == OWLRDFVocabulary.OWL_TOP_DATA_PROPERTY.get_iri()
diff --git a/owlapy/owlobject.py b/owlapy/owlobject.py
new file mode 100644
index 00000000..1e35d375
--- /dev/null
+++ b/owlapy/owlobject.py
@@ -0,0 +1,102 @@
+from abc import abstractmethod, ABCMeta
+from typing import Optional
+from .meta_classes import HasIRI
+
+class OWLObject(metaclass=ABCMeta):
+    """Base interface for OWL objects"""
+    __slots__ = ()
+
+    @abstractmethod
+    def __eq__(self, other):
+        pass
+
+    @abstractmethod
+    def __hash__(self):
+        pass
+
+    @abstractmethod
+    def __repr__(self):
+        pass
+
+    # default
+    def is_anonymous(self) -> bool:
+        return True
+
+
+
+class OWLObjectRenderer(metaclass=ABCMeta):
+    """Abstract class with a render method to render an OWL Object into a string."""
+    @abstractmethod
+    def set_short_form_provider(self, short_form_provider) -> None:
+        """Configure a short form provider that shortens the OWL objects during rendering.
+
+        Args:
+            short_form_provider: Short form provider.
+        """
+        pass
+
+    @abstractmethod
+    def render(self, o: OWLObject) -> str:
+        """Render OWL Object to string.
+
+        Args:
+            o: OWL Object.
+
+        Returns:
+            String rendition of OWL object.
+        """
+        pass
+
+
+class OWLObjectParser(metaclass=ABCMeta):
+    """Abstract class with a parse method to parse a string to an OWL Object."""
+    @abstractmethod
+    def parse_expression(self, expression_str: str) -> OWLObject:
+        """Parse a string to an OWL Object.
+
+        Args:
+            expression_str (str): Expression string.
+
+        Returns:
+            The OWL Object which is represented by the string.
+        """
+        pass
+
+
+class OWLNamedObject(OWLObject, HasIRI, metaclass=ABCMeta):
+    """Represents a named object for example, class, property, ontology etc. - i.e. anything that has an
+     IRI as its name."""
+    __slots__ = ()
+
+    _iri: 'IRI'
+
+    def __eq__(self, other):
+        if type(other) is type(self):
+            return self._iri == other._iri
+        return NotImplemented
+
+    def __lt__(self, other):
+        if type(other) is type(self):
+            return self._iri.as_str() < other._iri.as_str()
+        return NotImplemented
+
+    def __hash__(self):
+        return hash(self._iri)
+
+    def __repr__(self):
+        return f"{type(self).__name__}({repr(self._iri)})"
+
+    pass
+
+
+class OWLEntity(OWLNamedObject, metaclass=ABCMeta):
+    """Represents Entities in the OWL 2 Specification."""
+    __slots__ = ()
+
+    def to_string_id(self) -> str:
+        return self.get_iri().as_str()
+
+    def is_anonymous(self) -> bool:
+        return False
+
+    pass
diff --git a/owlapy/parser.py b/owlapy/parser.py
index f6f2b761..d9d5c552 100644
--- a/owlapy/parser.py
+++ b/owlapy/parser.py
@@ -4,20 +4,23 @@
 from parsimonious.grammar import Grammar
 from parsimonious.nodes import NodeVisitor
 from parsimonious.nodes import Node
-from owlapy.io import OWLObjectParser
-from owlapy.model import OWLObjectHasSelf, OWLObjectIntersectionOf, OWLObjectMinCardinality, OWLObjectOneOf, \
-    OWLObjectProperty, OWLObjectPropertyExpression, OWLObjectSomeValuesFrom, OWLObjectUnionOf, OWLClass, IRI, \
+from .owlobject import OWLObjectParser
+from .namespaces import Namespaces
+from .render import _DL_SYNTAX, _MAN_SYNTAX
+from .vocab import OWLFacet, OWLRDFVocabulary
+
+
+from owlapy.model import OWLObjectHasSelf, OWLObjectIntersectionOf, OWLObjectMinCardinality, OWLObjectProperty, OWLObjectPropertyExpression, OWLObjectSomeValuesFrom, OWLObjectUnionOf, OWLClass, IRI, \
     OWLClassExpression, OWLDataProperty, OWLNamedIndividual, OWLObjectComplementOf, OWLObjectExactCardinality, \
-    OWLObjectHasValue, OWLQuantifiedDataRestriction, OWLQuantifiedObjectRestriction, StringOWLDatatype,  \
+    OWLQuantifiedDataRestriction, OWLQuantifiedObjectRestriction, StringOWLDatatype,  \
     DateOWLDatatype, DateTimeOWLDatatype, DoubleOWLDatatype, DurationOWLDatatype, IntegerOWLDatatype, \
-    OWLDataSomeValuesFrom, OWLDatatypeRestriction, OWLFacetRestriction, OWLDataExactCardinality, \
-    OWLDataMaxCardinality, OWLObjectMaxCardinality, OWLDataIntersectionOf, OWLDataMinCardinality, OWLDataHasValue, \
-    OWLLiteral, OWLDataRange, OWLDataUnionOf, OWLDataOneOf, OWLDatatype, OWLObjectCardinalityRestriction, \
-    OWLDataCardinalityRestriction, OWLObjectAllValuesFrom, OWLDataAllValuesFrom, OWLDataComplementOf, BooleanOWLDatatype
-from owlapy.namespaces import Namespaces
+    OWLDataSomeValuesFrom, OWLDataExactCardinality, \
+    OWLDataMaxCardinality, OWLObjectMaxCardinality, OWLDataMinCardinality, OWLDataHasValue, \
+    OWLLiteral, OWLDataRange, OWLDataOneOf, OWLDatatype, OWLObjectCardinalityRestriction, \
+    OWLDataCardinalityRestriction, OWLObjectAllValuesFrom, OWLDataAllValuesFrom, BooleanOWLDatatype
 
-from owlapy.render import _DL_SYNTAX, _MAN_SYNTAX
-from owlapy.vocab import OWLFacet, OWLRDFVocabulary
+from owlapy.data_ranges import OWLDataIntersectionOf, OWLDataUnionOf, OWLDataComplementOf
+from owlapy.class_expression import OWLObjectHasValue, OWLDatatypeRestriction, OWLFacetRestriction, OWLObjectOneOf
 
 
 MANCHESTER_GRAMMAR = Grammar(r"""
@@ -764,3 +767,15 @@ def visit_parentheses(self, node, children) -> OWLClassExpression:
 
     def generic_visit(self, node, children):
         return children or node
+
+
+DLparser = DLSyntaxParser()
+ManchesterParser = ManchesterOWLSyntaxParser()
+
+
+def dl_to_owl_expression(dl_expression: str):
+    return DLparser.parse_expression(dl_expression)
+
+
+def manchester_to_owl_expression(manchester_expression: str):
+    return ManchesterParser.parse_expression(manchester_expression)
diff --git a/owlapy/render.py b/owlapy/render.py
index f68b1fb7..6a754f00 100644
--- a/owlapy/render.py
+++ b/owlapy/render.py
@@ -6,17 +6,22 @@
 from typing import List, Callable
 
 from owlapy import namespaces
-from owlapy.io import OWLObjectRenderer
-from owlapy.model import OWLLiteral, OWLNaryDataRange, OWLObject, OWLClass, OWLObjectSomeValuesFrom, \
-    OWLObjectAllValuesFrom, OWLObjectUnionOf, OWLBooleanClassExpression, OWLNaryBooleanClassExpression, \
-    OWLObjectIntersectionOf, OWLObjectComplementOf, OWLObjectInverseOf, OWLClassExpression, OWLRestriction, \
-    OWLObjectMinCardinality, OWLObjectExactCardinality, OWLObjectMaxCardinality, OWLObjectHasSelf, OWLObjectHasValue, \
-    OWLObjectOneOf, OWLNamedIndividual, OWLEntity, IRI, OWLPropertyExpression, OWLDataSomeValuesFrom, \
-    OWLFacetRestriction, OWLDatatypeRestriction, OWLDatatype, OWLDataAllValuesFrom, OWLDataComplementOf, \
-    OWLDataUnionOf, OWLDataIntersectionOf, OWLDataHasValue, OWLDataOneOf, OWLDataMaxCardinality, \
-    OWLDataMinCardinality, OWLDataExactCardinality
+from .owlobject import OWLObjectRenderer
+from .owl_property import OWLObjectInverseOf
+from .class_expression import OWLClassExpression, OWLBooleanClassExpression
+
+from owlapy.model import (OWLLiteral, OWLObject, OWLClass, OWLObjectSomeValuesFrom, \
+    OWLObjectAllValuesFrom, OWLObjectUnionOf, OWLNaryBooleanClassExpression, \
+    OWLObjectIntersectionOf, OWLObjectComplementOf, OWLRestriction, \
+    OWLObjectMinCardinality, OWLObjectExactCardinality, OWLObjectMaxCardinality, OWLObjectHasSelf,
+                          OWLNamedIndividual, OWLEntity, IRI, OWLPropertyExpression, OWLDataSomeValuesFrom, \
+    OWLDatatype, OWLDataAllValuesFrom, \
+    OWLDataHasValue, OWLDataOneOf, OWLDataMaxCardinality, \
+    OWLDataMinCardinality, OWLDataExactCardinality)
 from owlapy.vocab import OWLFacet
 
+from .data_ranges import OWLNaryDataRange, OWLDataComplementOf, OWLDataUnionOf, OWLDataIntersectionOf
+from .class_expression import OWLObjectHasValue, OWLFacetRestriction, OWLDatatypeRestriction, OWLObjectOneOf
 
 _DL_SYNTAX = types.SimpleNamespace(
     SUBCLASS="⊑",
@@ -142,7 +147,7 @@ def _(self, r: OWLObjectHasValue):
 
     @render.register
     def _(self, r: OWLObjectOneOf):
-        return "{%s}" % (" %s " % _DL_SYNTAX.OR).join(
+        return "{%s}" % (" %s " % _DL_SYNTAX.COMMA).join(
             "%s" % (self.render(_)) for _ in r.individuals())
 
     @render.register
@@ -420,3 +425,15 @@ def _render_nested(self, c: OWLClassExpression) -> str:
             return "(%s)" % self.render(c)
         else:
             return self.render(c)
+
+
+DLrenderer = DLSyntaxObjectRenderer()
+ManchesterRenderer = ManchesterOWLSyntaxOWLObjectRenderer()
+
+
+def owl_expression_to_dl(o: OWLObject) -> str:
+    return DLrenderer.render(o)
+
+
+def owl_expression_to_manchester(o: OWLObject) -> str:
+    return ManchesterRenderer.render(o)
diff --git a/owlapy/types.py b/owlapy/types.py
new file mode 100644
index 00000000..59884809
--- /dev/null
+++ b/owlapy/types.py
@@ -0,0 +1,29 @@
+from .owlobject import OWLObject, OWLEntity
+from .data_ranges import OWLPropertyRange, OWLDataRange
+from .iri import IRI
+from .meta_classes import HasIRI
+from typing import Final, Union
+
+class OWLDatatype(OWLEntity, OWLDataRange):
+    """Represents a Datatype (named data range) in the OWL 2 Specification."""
+    __slots__ = '_iri'
+
+    type_index: Final = 4001
+
+    _iri: IRI
+
+    def __init__(self, iri: Union[IRI, HasIRI]):
+        """Gets an instance of OWLDatatype that has the specified IRI.
+
+        Args:
+            iri: The IRI.
+        """
+        if isinstance(iri, HasIRI):
+            self._iri = iri.get_iri()
+        else:
+            assert isinstance(iri, IRI)
+            self._iri = iri
+
+    def get_iri(self) -> IRI:
+        # documented in parent
+        return self._iri
diff --git a/owlapy/util.py b/owlapy/util.py
index a5000b8c..7d1c77b1 100644
--- a/owlapy/util.py
+++ b/owlapy/util.py
@@ -1,17 +1,18 @@
 """Owlapy utils."""
 from functools import singledispatchmethod, total_ordering
 from typing import Iterable, List, Type, TypeVar, Generic, Tuple, cast, Optional, Union, overload
-
-from owlapy.model import HasIndex, HasIRI, OWLClassExpression, OWLClass, OWLObjectCardinalityRestriction, \
+from .has import HasIndex
+from .owl_property import OWLObjectInverseOf
+from owlapy.model import HasIRI, OWLClassExpression, OWLClass, OWLObjectCardinalityRestriction, \
     OWLObjectComplementOf, OWLNothing, OWLPropertyRange, OWLRestriction, OWLThing, OWLObjectSomeValuesFrom, \
-    OWLObjectHasValue, OWLObjectMinCardinality, OWLObjectMaxCardinality, OWLObjectExactCardinality, OWLObjectHasSelf, \
-    OWLObjectOneOf, OWLDataMaxCardinality, OWLDataMinCardinality, OWLDataExactCardinality, OWLDataHasValue, \
+    OWLObjectMinCardinality, OWLObjectMaxCardinality, OWLObjectExactCardinality, OWLObjectHasSelf, \
+    OWLDataMaxCardinality, OWLDataMinCardinality, OWLDataExactCardinality, OWLDataHasValue, \
     OWLDataAllValuesFrom, OWLDataSomeValuesFrom, OWLObjectAllValuesFrom, HasFiller, HasCardinality, HasOperands, \
-    OWLObjectInverseOf, OWLDatatypeRestriction, OWLDataComplementOf, OWLDatatype, OWLDataUnionOf, \
-    OWLDataIntersectionOf, OWLDataOneOf, OWLFacetRestriction, OWLLiteral, OWLObjectIntersectionOf, \
-    OWLDataCardinalityRestriction, OWLNaryBooleanClassExpression, OWLNaryDataRange, OWLObjectUnionOf, \
+    OWLDatatype,OWLDataOneOf, OWLLiteral, OWLObjectIntersectionOf, \
+    OWLDataCardinalityRestriction, OWLNaryBooleanClassExpression, OWLObjectUnionOf, \
     OWLDataRange, OWLObject
-
+from .data_ranges import OWLDataComplementOf, OWLDataUnionOf, OWLDataIntersectionOf, OWLNaryDataRange
+from .class_expression import OWLObjectHasValue, OWLDatatypeRestriction, OWLFacetRestriction, OWLObjectOneOf
 
 _HasIRI = TypeVar('_HasIRI', bound=HasIRI)  #:
 _HasIndex = TypeVar('_HasIndex', bound=HasIndex)  #:
diff --git a/owlapy/vocab.py b/owlapy/vocab.py
index cd175a8d..59e492c5 100644
--- a/owlapy/vocab.py
+++ b/owlapy/vocab.py
@@ -6,7 +6,8 @@
 from re import match
 
 from owlapy import namespaces
-from owlapy.model._iri import HasIRI, IRI
+from .meta_classes import HasIRI
+from .iri import IRI
 from owlapy.namespaces import Namespaces
 
 
diff --git a/setup.py b/setup.py
index 5f508633..4c138d64 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@
     name="owlapy",
     description="Owlapy is loosely based on owlapi - the java counterpart, "
                 "successfully representing the main owl objects in python.",
-    version="0.1.2",
+    version="0.1.3",
     packages=find_packages(),
     install_requires=[
         "pandas>=1.5.0",


Test patch:
diff --git a/tests/test_class_expression_semantics.py b/tests/test_class_expression_semantics.py
new file mode 100644
index 00000000..ad2dbb46
--- /dev/null
+++ b/tests/test_class_expression_semantics.py
@@ -0,0 +1,27 @@
+from owlapy.iri import IRI
+
+from owlapy.class_expression import OWLClass, OWLObjectComplementOf, OWLObjectUnionOf
+from owlapy.class_expression import OWLBooleanClassExpression, OWLObjectIntersectionOf, OWLClassExpression
+from owlapy.owl_property import OWLObjectProperty
+from owlapy.class_expression import OWLObjectSomeValuesFrom, OWLObjectAllValuesFrom
+
+from owlapy.owl2sparql.converter import owl_expression_to_sparql
+from owlapy.render import owl_expression_to_dl
+
+
+class TestClassExpression:
+    def test_iri(self):
+        # Create the male class
+        C = OWLClass("http://example.com/society#C")
+        assert isinstance(C, OWLClassExpression)
+        Not_C=OWLObjectComplementOf(C)
+        assert isinstance(Not_C, OWLClassExpression)
+        C_AND_C=OWLObjectIntersectionOf([C, C])
+        assert isinstance(C_AND_C, OWLClassExpression)
+        C_OR_C = OWLObjectUnionOf([C, C])
+        assert isinstance(C_OR_C, OWLClassExpression)
+
+        hasChild = OWLObjectProperty("http://example.com/society#hasChild")
+        assert isinstance(OWLObjectSomeValuesFrom(hasChild, C), OWLClassExpression)
+
+        assert isinstance(OWLObjectAllValuesFrom(hasChild, C), OWLClassExpression)
diff --git a/tests/test_examples.py b/tests/test_examples.py
new file mode 100644
index 00000000..cb3d60d7
--- /dev/null
+++ b/tests/test_examples.py
@@ -0,0 +1,30 @@
+
+from owlapy.iri import IRI
+from owlapy.class_expression import OWLClass, OWLObjectIntersectionOf
+from owlapy.owl_property import OWLObjectProperty
+from owlapy.class_expression import OWLObjectSomeValuesFrom
+from owlapy.owl2sparql.converter import owl_expression_to_sparql
+from owlapy.render import owl_expression_to_dl
+
+class TestRunningExamples:
+    def test_readme(self):
+        # Create the male class
+        male = OWLClass("http://example.com/society#male")
+
+        # Create an object property using the iri as a string for 'hasChild' property.
+        hasChild = OWLObjectProperty("http://example.com/society#hasChild")
+
+        # Create an existential restrictions
+        males_with_children = OWLObjectSomeValuesFrom(hasChild, male)
+
+        # Let's make it more complex by intersecting with another class
+        teacher = OWLClass("http://example.com/society#teacher")
+        male_teachers_with_children = OWLObjectIntersectionOf([males_with_children, teacher])
+
+        assert owl_expression_to_dl(male_teachers_with_children)=="(∃ hasChild.male) ⊓ teacher"
+        assert owl_expression_to_sparql("?x", male_teachers_with_children)=="""SELECT
+ DISTINCT ?x WHERE { 
+?x <http://example.com/society#hasChild> ?s_1 . 
+?s_1 a <http://example.com/society#male> . 
+?x a <http://example.com/society#teacher> . 
+ }"""
\ No newline at end of file
diff --git a/tests/test_owlapy_cnf_dnf.py b/tests/test_owlapy_cnf_dnf.py
index ffbfd525..f8432df9 100644
--- a/tests/test_owlapy_cnf_dnf.py
+++ b/tests/test_owlapy_cnf_dnf.py
@@ -2,10 +2,10 @@
 
 from owlapy.model import OWLObjectProperty, OWLObjectSomeValuesFrom, OWLObjectUnionOf, \
     OWLClass, IRI, OWLDataProperty, OWLDataSomeValuesFrom, OWLNamedIndividual, OWLObjectComplementOf, \
-    OWLObjectIntersectionOf, OWLObjectMinCardinality, OWLObjectOneOf
+    OWLObjectIntersectionOf, OWLObjectMinCardinality
 from owlapy.model.providers import OWLDatatypeMinExclusiveRestriction
 from owlapy.util import TopLevelCNF, TopLevelDNF
-
+from owlapy.class_expression import OWLObjectOneOf
 
 class TopLevelNFTest(unittest.TestCase):
 
diff --git a/tests/test_owlapy_nnf.py b/tests/test_owlapy_nnf.py
index 4d772dc4..60d34b02 100644
--- a/tests/test_owlapy_nnf.py
+++ b/tests/test_owlapy_nnf.py
@@ -26,13 +26,14 @@
 
 from owlapy.model import OWLObjectProperty, OWLNamedIndividual, OWLObjectComplementOf, \
     OWLObjectAllValuesFrom, OWLObjectSomeValuesFrom, OWLObjectIntersectionOf, OWLObjectUnionOf, \
-    OWLObjectMinCardinality, OWLObjectMaxCardinality, OWLObjectHasValue, OWLObjectOneOf, OWLClassExpression, IRI, \
-    BooleanOWLDatatype, DoubleOWLDatatype, IntegerOWLDatatype, OWLClass, OWLDataAllValuesFrom, OWLDataComplementOf, \
-    OWLDataIntersectionOf, OWLDataProperty, OWLDataSomeValuesFrom, OWLDataUnionOf, \
-    OWLDataHasValue, OWLDataMaxCardinality, OWLDataMinCardinality, OWLDataOneOf, OWLLiteral
+    OWLObjectMinCardinality, OWLObjectMaxCardinality, OWLClassExpression, IRI, \
+    BooleanOWLDatatype, DoubleOWLDatatype, IntegerOWLDatatype, OWLClass, OWLDataAllValuesFrom, \
+    OWLDataProperty, OWLDataSomeValuesFrom,OWLDataHasValue, OWLDataMaxCardinality, OWLDataMinCardinality, OWLDataOneOf, OWLLiteral
 from owlapy.model.providers import OWLDatatypeMinMaxExclusiveRestriction
 from owlapy.util import NNF
 
+from owlapy.data_ranges import OWLDataComplementOf, OWLDataIntersectionOf, OWLDataUnionOf
+from owlapy.class_expression import OWLObjectHasValue, OWLObjectOneOf
 
 def iri(suffix):
     NS = "http://example.org/"
diff --git a/tests/test_owlapy_parser.py b/tests/test_owlapy_parser.py
index 413f3fc3..370b32e3 100644
--- a/tests/test_owlapy_parser.py
+++ b/tests/test_owlapy_parser.py
@@ -2,16 +2,19 @@
 from datetime import date, datetime, timedelta, timezone
 
 from pandas import Timedelta
-from owlapy.model import OWLObjectInverseOf, OWLObjectMinCardinality, OWLObjectSomeValuesFrom, \
-    OWLObjectUnionOf, DoubleOWLDatatype, IntegerOWLDatatype, OWLClass, IRI, OWLDataAllValuesFrom, \
-    OWLDataIntersectionOf, OWLDataOneOf, OWLDataProperty, OWLDataSomeValuesFrom, OWLDatatypeRestriction,  \
+from owlapy.owl_property import OWLObjectInverseOf
+
+from owlapy.model import OWLObjectUnionOf, DoubleOWLDatatype, IntegerOWLDatatype, OWLClass, IRI, OWLDataAllValuesFrom, \
+    OWLDataOneOf, OWLDataProperty, \
     OWLLiteral, OWLNamedIndividual, OWLObjectAllValuesFrom, OWLObjectComplementOf, OWLObjectExactCardinality, \
-    OWLObjectHasSelf, OWLObjectHasValue, OWLObjectIntersectionOf, OWLObjectMaxCardinality, OWLObjectOneOf, \
-    OWLObjectProperty, OWLDataComplementOf, OWLDataExactCardinality, OWLDataMaxCardinality, OWLDataUnionOf, \
-    OWLDataMinCardinality, OWLDataHasValue, OWLThing, OWLNothing, OWLFacetRestriction
+    OWLObjectHasSelf, OWLObjectIntersectionOf, OWLObjectMaxCardinality, OWLObjectProperty, OWLDataExactCardinality, OWLDataMaxCardinality, \
+    OWLDataMinCardinality, OWLDataHasValue, OWLThing, OWLNothing
 
+from owlapy.data_ranges import OWLDataIntersectionOf, OWLDataComplementOf, OWLDataUnionOf
 from owlapy.model.providers import OWLDatatypeMinExclusiveRestriction,\
     OWLDatatypeMinMaxExclusiveRestriction, OWLDatatypeMaxExclusiveRestriction
+from owlapy.class_expression import OWLDataSomeValuesFrom, OWLDatatypeRestriction, OWLFacetRestriction, OWLObjectSomeValuesFrom, OWLObjectMinCardinality, OWLObjectHasValue,OWLObjectOneOf
+
 from owlapy.parser import DLSyntaxParser, ManchesterOWLSyntaxParser
 from owlapy.vocab import OWLFacet
 
diff --git a/tests/test_owlapy_render.py b/tests/test_owlapy_render.py
index 5ecec7fb..b0a67fd1 100644
--- a/tests/test_owlapy_render.py
+++ b/tests/test_owlapy_render.py
@@ -1,14 +1,15 @@
 import unittest
-
+from owlapy.owl_property import OWLObjectProperty
 from owlapy.model import OWLDataMinCardinality, OWLObjectIntersectionOf, OWLObjectSomeValuesFrom, \
-    OWLThing, OWLObjectComplementOf, OWLObjectUnionOf, OWLNamedIndividual, OWLObjectOneOf, OWLObjectHasValue, \
-    OWLObjectMinCardinality, IRI, OWLDataProperty, DoubleOWLDatatype, OWLClass, OWLDataComplementOf, \
-    OWLDataIntersectionOf, IntegerOWLDatatype, OWLDataExactCardinality, OWLDataHasValue, OWLDataAllValuesFrom, \
-    OWLDataOneOf, OWLDataSomeValuesFrom, OWLDataUnionOf, OWLLiteral, OWLObjectProperty, BooleanOWLDatatype, \
+    OWLThing, OWLObjectComplementOf, OWLObjectUnionOf, OWLNamedIndividual, OWLObjectMinCardinality, IRI, OWLDataProperty, DoubleOWLDatatype, OWLClass, \
+    IntegerOWLDatatype, OWLDataExactCardinality, OWLDataHasValue, OWLDataAllValuesFrom, \
+    OWLDataOneOf, OWLDataSomeValuesFrom, OWLLiteral, BooleanOWLDatatype, \
     OWLDataMaxCardinality
+
+from owlapy.data_ranges import OWLDataComplementOf, OWLDataIntersectionOf, OWLDataUnionOf
 from owlapy.model.providers import OWLDatatypeMinMaxInclusiveRestriction
 from owlapy.render import DLSyntaxObjectRenderer, ManchesterOWLSyntaxOWLObjectRenderer
-
+from owlapy.class_expression import OWLObjectHasValue, OWLObjectOneOf
 
 class Owlapy_DLRenderer_Test(unittest.TestCase):
     def test_ce_render(self):
@@ -43,7 +44,8 @@ def test_ce_render(self):
         oneof = OWLObjectOneOf((i1, i2))
         r = renderer.render(oneof)
         print(r)
-        self.assertEqual(r, "{heinz ⊔ marie}")
+
+        self.assertEqual(r, "{heinz , marie}")
 
         hasvalue = OWLObjectHasValue(property=has_child, individual=i1)
         r = renderer.render(hasvalue)