All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.jena.ontapi.impl.objects.OntClassImpl Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jena.ontapi.impl.objects;

import org.apache.jena.datatypes.RDFDatatype;
import org.apache.jena.datatypes.xsd.XSDDatatype;
import org.apache.jena.enhanced.EnhGraph;
import org.apache.jena.graph.Node;
import org.apache.jena.ontapi.OntJenaException;
import org.apache.jena.ontapi.OntModelControls;
import org.apache.jena.ontapi.common.OntEnhGraph;
import org.apache.jena.ontapi.common.OntEnhNodeFactories;
import org.apache.jena.ontapi.common.OntPersonalities;
import org.apache.jena.ontapi.impl.HierarchySupport;
import org.apache.jena.ontapi.impl.OntGraphModelImpl;
import org.apache.jena.ontapi.model.OntClass;
import org.apache.jena.ontapi.model.OntDataProperty;
import org.apache.jena.ontapi.model.OntDataRange;
import org.apache.jena.ontapi.model.OntDisjoint;
import org.apache.jena.ontapi.model.OntIndividual;
import org.apache.jena.ontapi.model.OntList;
import org.apache.jena.ontapi.model.OntModel;
import org.apache.jena.ontapi.model.OntObject;
import org.apache.jena.ontapi.model.OntObjectProperty;
import org.apache.jena.ontapi.model.OntProperty;
import org.apache.jena.ontapi.model.OntRelationalProperty;
import org.apache.jena.ontapi.model.OntStatement;
import org.apache.jena.ontapi.utils.Iterators;
import org.apache.jena.ontapi.utils.StdModels;
import org.apache.jena.rdf.model.Literal;
import org.apache.jena.rdf.model.Property;
import org.apache.jena.rdf.model.RDFList;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.ResourceFactory;
import org.apache.jena.util.iterator.ExtendedIterator;
import org.apache.jena.vocabulary.OWL2;
import org.apache.jena.vocabulary.RDF;
import org.apache.jena.vocabulary.RDFS;

import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * A base class for any class-expression implementation.
 */
@SuppressWarnings("WeakerAccess")
public abstract class OntClassImpl extends OntObjectImpl implements OntClass {

    public OntClassImpl(Node n, EnhGraph m) {
        super(n, m);
    }

    public static boolean isQualified(OntObject c) {
        return c != null &&
                OntGraphModelImpl.configValue(c.getModel(), OntModelControls.USE_OWL2_QUALIFIED_CARDINALITY_RESTRICTION_FEATURE)
                && !(OWL2.Thing.equals(c) || RDFS.Literal.equals(c));
    }

    protected static CardinalityType getCardinalityType(Class> view) {
        if (ObjectMinCardinality.class.equals(view) || DataMinCardinality.class.equals(view)) {
            return CardinalityType.MIN;
        }
        if (ObjectMaxCardinality.class.equals(view) || DataMaxCardinality.class.equals(view)) {
            return CardinalityType.MAX;
        }
        return CardinalityType.EXACTLY;
    }

    protected static Literal createNonNegativeIntegerLiteral(int n) {
        if (n < 0) throw new OntJenaException.IllegalArgument("Can't accept negative value: " + n);
        return ResourceFactory.createTypedLiteral(String.valueOf(n), XSDDatatype.XSDnonNegativeInteger);
    }

    private static Resource createRestriction(OntModel model) {
        return model.createResource(OWL2.Restriction);
    }

    protected static Resource createOnPropertyRestriction(OntGraphModelImpl model, OntProperty onProperty) {
        OntJenaException.notNull(onProperty, "Null property.");
        return createRestriction(model).addProperty(OWL2.onProperty, onProperty);
    }

    public static > CE createComponentRestrictionCE(OntGraphModelImpl model,
                                                                                          Class view,
                                                                                          OntProperty onProperty,
                                                                                          RDFNode other,
                                                                                          Property predicate) {
        OntJenaException.notNull(other, "Null expression.");
        Resource res = createOnPropertyRestriction(model, onProperty).addProperty(predicate, other);
        return model.getNodeAs(res.asNode(), view);
    }

    public static > CE createCardinalityRestrictionCE(OntGraphModelImpl model,
                                                                                              Class view,
                                                                                              OntProperty onProperty,
                                                                                              int cardinality,
                                                                                              OntObject object) {
        Literal value = createNonNegativeIntegerLiteral(cardinality);
        Resource res = createOnPropertyRestriction(model, onProperty);
        boolean qualified = isQualified(object);
        model.add(res, getCardinalityType(view).getPredicate(qualified), value);
        if (qualified) {
            model.add(res, onProperty instanceof OntObjectProperty ? OWL2.onClass : OWL2.onDataRange, object);
        }
        return model.getNodeAs(res.asNode(), view);
    }

    public static > CE createNaryRestrictionCE(OntGraphModelImpl model,
                                                                                Class type,
                                                                                OntDataRange dr,
                                                                                Collection properties) {
        NaryRestrictionImpl.validateArity(dr, properties);
        Property predicate = NaryDataAllValuesFrom.class.equals(type) ? OWL2.allValuesFrom : OWL2.someValuesFrom;
        Resource res = createRestriction(model)
                .addProperty(predicate, dr)
                .addProperty(OWL2.onProperties, model.createList(properties.iterator()));
        return model.getNodeAs(res.asNode(), type);
    }

    public static , R extends OntObject> CE createComponentsCE(OntGraphModelImpl model,
                                                                                          Class returnType,
                                                                                          Class componentType,
                                                                                          Property predicate,
                                                                                          Stream components) {
        OntJenaException.notNull(components, "Null components stream.");
        RDFList items = model.createList(components
                .peek(x -> OntJenaException.notNull(x,
                        OntEnhNodeFactories.viewAsString(returnType) + ": null " + OntEnhNodeFactories.viewAsString(componentType) + " member"))
                .iterator());
        Resource res = model.createResource(OWL2.Class).addProperty(predicate, items);
        return model.getNodeAs(res.asNode(), returnType);
    }

    public static HasSelf createHasSelf(OntGraphModelImpl model, OntObjectProperty onProperty) {
        Resource res = createOnPropertyRestriction(model, onProperty).addProperty(OWL2.hasSelf, StdModels.TRUE);
        return model.getNodeAs(res.asNode(), HasSelf.class);
    }

    public static ComplementOf createComplementOf(OntGraphModelImpl model, OntClass other) {
        OntJenaException.notNull(other, "Null class expression.");
        Resource res = model.createResource(OWL2.Class).addProperty(OWL2.complementOf, other);
        return model.getNodeAs(res.asNode(), ComplementOf.class);
    }

    public static OntIndividual.Anonymous createAnonymousIndividual(OntGraphModelImpl model, OntClass source) {
        model.checkType(OntIndividual.Anonymous.class);
        OntJenaException.checkSupported(source.canAsAssertionClass(),
                "Class " + OntEnhNodeFactories.viewAsString(source.getClass()) + " cannot have individuals. " +
                        "Profile: " + model.getOntPersonality().getName());
        return model.getNodeAs(model.createResource(source).asNode(), OntIndividual.Anonymous.class);
    }

    public static OntIndividual.Named createNamedIndividual(OntGraphModelImpl model, OntClass source, String uri) {
        OntJenaException.notNull(uri, "Null uri");
        OntJenaException.checkSupported(source.canAsAssertionClass(),
                "Class " + OntEnhNodeFactories.viewAsString(source.getClass()) + " cannot have individuals. " +
                        "Profile: " + model.getOntPersonality().getName());
        Resource res = model.createResource(uri, source);
        if (OntGraphModelImpl.configValue(model, OntModelControls.USE_OWL2_NAMED_INDIVIDUAL_DECLARATION_FEATURE)) {
            res.addProperty(RDF.type, OWL2.NamedIndividual);
        }
        return res.as(OntIndividual.Named.class);
    }

    public static OntList createHasKey(OntGraphModelImpl m,
                                                              OntClass clazz,
                                                              Stream collection) {
        OntGraphModelImpl.checkFeature(m, OntModelControls.USE_OWL2_CLASS_HAS_KEY_FEATURE, "owl:hasKey");
        OntJenaException.checkSupported(clazz.canAsSubClass(),
                "Class " + OntEnhNodeFactories.viewAsString(clazz.getClass()) + " cannot have keys. " +
                        "Profile: " + m.getOntPersonality().getName());
        return m.createOntList(clazz, OWL2.hasKey, OntRelationalProperty.class,
                collection.distinct().map(OntRelationalProperty.class::cast).iterator());
    }

    public static Stream> listHasKeys(OntGraphModelImpl m, OntClass clazz) {
        if (!OntGraphModelImpl.configValue(m, OntModelControls.USE_OWL2_CLASS_HAS_KEY_FEATURE)) {
            return Stream.empty();
        }
        if (!clazz.canAsSubClass()) {
            return Stream.empty();
        }
        return OntListImpl.stream(m, clazz, OWL2.hasKey, OntRelationalProperty.class);
    }

    public static void removeHasKey(OntGraphModelImpl m,
                                    OntClass clazz,
                                    RDFNode rdfList) throws OntJenaException.IllegalArgument {
        OntGraphModelImpl.checkFeature(m, OntModelControls.USE_OWL2_CLASS_HAS_KEY_FEATURE, "owl:hasKey");
        if (rdfList == null) {
            clazz.hasKeys().toList().forEach(it -> m.deleteOntList(clazz, OWL2.hasKey, it));
        } else {
            m.deleteOntList(clazz, OWL2.hasKey, clazz.findHasKey(rdfList)
                    .orElseThrow(() -> new OntJenaException.IllegalArgument("can't find list " + rdfList)));
        }
    }

    public static Stream disjointClasses(OntGraphModelImpl m, OntClass clazz) {
        if (!OntGraphModelImpl.configValue(m, OntModelControls.USE_OWL_CLASS_DISJOINT_WITH_FEATURE)) {
            return Stream.empty();
        }
        if (!clazz.canAsDisjointClass()) {
            return Stream.empty();
        }
        return Stream.of(
                        clazz.objects(OWL2.disjointWith, OntClass.class),
                        m.statements(null, OWL2.disjointWith, clazz)
                                .filter(it -> it.getSubject().canAs(OntClass.class))
                                .map(it -> it.getSubject(OntClass.class)),
                        clazz.disjoints().flatMap(OntDisjoint::members)
                )
                .flatMap(it -> it)
                .filter(it -> !it.equals(clazz))
                .filter(OntClass::canAsDisjointClass)
                .map(OntClass::asDisjointClass)
                .distinct();
    }

    public static void addDisjoint(OntGraphModelImpl m, OntClass clazz, OntClass other) {
        OntGraphModelImpl.checkFeature(m, OntModelControls.USE_OWL_CLASS_DISJOINT_WITH_FEATURE, "owl:disjointWith");
        OntJenaException.checkSupported(clazz.canAsDisjointClass() && other.canAsDisjointClass(),
                "Classes " + OntEnhNodeFactories.viewAsString(clazz.getClass()) + " and " + OntEnhNodeFactories.viewAsString(other.getClass()) +
                        " cannot be disjoint. Profile " + m.getOntPersonality().getName()
        );
        clazz.addStatement(OWL2.disjointWith, other);
    }

    public static void removeDisjoint(OntGraphModelImpl m, OntClass clazz, Resource other) {
        OntGraphModelImpl.checkFeature(m, OntModelControls.USE_OWL_CLASS_DISJOINT_WITH_FEATURE, "owl:disjointWith");
        Objects.requireNonNull(clazz).remove(OWL2.disjointWith, other);
    }

    public static OntStatement addDisjointWithStatement(OntGraphModelImpl m, OntClass clazz, OntClass other) {
        OntGraphModelImpl.checkFeature(m, OntModelControls.USE_OWL_CLASS_DISJOINT_WITH_FEATURE, "owl:disjointWith");
        OntJenaException.checkSupported(clazz.canAsDisjointClass() && other.canAsDisjointClass(),
                "Classes " + OntEnhNodeFactories.viewAsString(clazz.getClass()) + " and " + OntEnhNodeFactories.viewAsString(other.getClass()) +
                        " cannot be disjoint. Profile " + m.getOntPersonality().getName()
        );
        return clazz.addStatement(OWL2.disjointWith, other);
    }

    public static Stream equivalentClasses(OntGraphModelImpl m, OntClass clazz) {
        if (!OntGraphModelImpl.configValue(m, OntModelControls.USE_OWL_CLASS_EQUIVALENT_FEATURE)) {
            return Stream.empty();
        }
        if (!clazz.canAsEquivalentClass()) {
            return Stream.empty();
        }
        return Stream.of(clazz.objects(OWL2.equivalentClass, OntClass.class),
                        m.statements(null, OWL2.equivalentClass, clazz)
                                .filter(it -> it.getSubject().canAs(OntClass.class))
                                .map(it -> it.getSubject(OntClass.class))
                )
                .flatMap(it -> it)
                .filter(it -> !it.equals(clazz))
                .filter(OntClass::canAsEquivalentClass)
                .map(OntClass::asEquivalentClass)
                .distinct();
    }

    public static OntStatement addEquivalentClass(OntGraphModelImpl m, OntClass clazz, OntClass other) {
        OntGraphModelImpl.checkFeature(m, OntModelControls.USE_OWL_CLASS_EQUIVALENT_FEATURE, "owl:equivalentClass");
        OntJenaException.checkSupported(clazz.canAsEquivalentClass() && other.canAsEquivalentClass(),
                "Classes " + OntEnhNodeFactories.viewAsString(clazz.getClass()) + " and " + OntEnhNodeFactories.viewAsString(other.getClass()) +
                        " cannot be equivalent. Profile " + m.getOntPersonality().getName()
        );
        return clazz.addStatement(OWL2.equivalentClass, other);
    }

    public static void removeEquivalentClass(OntGraphModelImpl m, OntClass clazz, Resource other) {
        OntGraphModelImpl.checkFeature(m, OntModelControls.USE_OWL_CLASS_EQUIVALENT_FEATURE, "owl:equivalentClass");
        clazz.remove(OWL2.equivalentClass, other);
    }

    public static Stream declaredProperties(OntClass clazz, boolean direct) {
        OntModel m = clazz.getModel();
        Stream properties = OntPersonalities.isRDFS(OntEnhGraph.asPersonalityModel(m).getOntPersonality())
                ? m.ontObjects(OntProperty.class)
                : Stream.of(m.objectProperties(), m.dataProperties(), m.annotationProperties()).flatMap(it -> it);
        Map> indirectSuperclasses = new HashMap<>();
        Function> getIndirectSuperclasses = it ->
                indirectSuperclasses.computeIfAbsent(it, x -> x.superClasses(false).collect(Collectors.toSet()));
        return properties.distinct().filter(p -> p != null && testDomain(clazz, p, direct, getIndirectSuperclasses));
    }

    /**
     * Answers {@code true} if the {@code clazz} is a domain of {@code property}.
     *
     * @param clazz    {@link OntClass}
     * @param property {@link OntProperty}
     * @param direct   {@code boolean}
     * @return {@code boolean}
     */
    public static boolean testDomain(OntClass clazz, OntProperty property, boolean direct) {
        return testDomain(clazz, property, direct, x -> x.superClasses(false).collect(Collectors.toSet()));
    }

    protected static boolean testDomain(OntClass clazz,
                                        OntProperty property,
                                        boolean direct,
                                        Function> getIndirectSuperClasses) {
        if (isReservedOrBuiltin(property)) {
            return false;
        }
        AtomicBoolean isGlobal = new AtomicBoolean(true);
        AtomicBoolean seenDirect = new AtomicBoolean(false);
        try (Stream domains = property.domains()) {
            if (!domains.allMatch(domain -> {
                if (domain.equals(OWL2.Thing) || domain.equals(RDFS.Resource)) {
                    // there are some well-known values we ignore
                    return true;
                }
                isGlobal.set(false);
                if (clazz.equals(domain)) {
                    // if this class is actually in the domain (as opposed to one of this class's
                    // superclasses), then we've detected the direct property case
                    seenDirect.set(true);
                } else {
                    // there is a class in the domain of p that is not a superclass of this class
                    return canProveSuperClass(clazz, domain, getIndirectSuperClasses);
                }
                return true;
            })) {
                return false;
            }
            if (direct) {
                // if we're looking for direct props, we must either have seen the direct case
                // or it's a global prop and this is a root class
                return seenDirect.get() || (isGlobal.get() && clazz.isHierarchyRoot());
            }
            // not direct, we must either found a global or a superclass prop
            // otherwise the 'return false' above would have kicked in
            return true;
        }
    }

    /**
     * Answers true
     * if we can demonstrate
     * that the class specified as first parameter class has the second parameter as a superclass.
     * If this model has a reasoner, this is equivalent to asking if the subclass relation holds.
     * Otherwise, we simulate basic reasoning by searching upwards through the class hierarchy.
     *
     * @param clazz                   potential subclass
     * @param candidate               for superclass of {@code clazz}
     * @param getIndirectSuperClasses a function to get indirect superclasses
     * @return {@code true} if we can show that {@code candidate} is a superclass of {@code clazz}
     */
    protected static boolean canProveSuperClass(OntClass clazz,
                                                Resource candidate,
                                                Function> getIndirectSuperClasses) {
        Set seen = new HashSet<>();
        Deque queue = new ArrayDeque<>();
        queue.add(clazz);
        while (!queue.isEmpty()) {
            OntClass current = queue.removeFirst();
            if (seen.contains(current)) {
                continue;
            }
            seen.add(current);
            if (current.equals(candidate)) {
                return true;
            }
            for (OntClass next : getIndirectSuperClasses.apply(current)) {
                if (next.equals(candidate)) {
                    return true;
                }
                queue.add(next);
            }
        }
        return false;
    }

    public static boolean isHierarchyRoot(OntClass clazz) {
        if (OWL2.Nothing.equals(clazz)) {
            return false;
        }
        try (Stream superClasses = clazz.superClasses(true)) {
            return superClasses.allMatch(s -> s.equals(OWL2.Thing) || s.equals(RDFS.Resource) || s.equals(clazz));
        }
    }

    public static boolean isDisjoint(OntClass clazz, Resource candidate) {
        if (!candidate.canAs(OntClass.class)) {
            return false;
        }
        if (!clazz.canAsDisjointClass()) {
            return false;
        }
        OntClass other = candidate.as(OntClass.class);
        if (!other.canAsDisjointClass()) {
            return false;
        }
        try (Stream disjoints = other.disjointClasses()) {
            if (disjoints.anyMatch(clazz::equals)) {
                return true;
            }
        }
        try (Stream disjoints = clazz.disjointClasses()) {
            if (disjoints.anyMatch(other::equals)) {
                return true;
            }
        }
        try (Stream disjoints = clazz.getModel().ontObjects(OntDisjoint.Classes.class)) {
            return disjoints.anyMatch(d -> {
                Set members = d.members().collect(Collectors.toSet());
                return members.contains(clazz) && members.contains(other);
            });
        }
    }

    static Stream individuals(OntClass clazz, boolean direct) {
        if (!clazz.canAsAssertionClass()) {
            return Stream.empty();
        }
        if (OntGraphModelImpl.configValue(clazz.getModel(), OntModelControls.USE_BUILTIN_HIERARCHY_SUPPORT)) {
            // TODO: optimize
            return clazz.getModel().individuals().filter(i -> i.hasOntClass(clazz, direct));
        }
        return subjects(RDF.type, clazz, OntIndividual.class).filter(i -> i.hasOntClass(clazz, direct));
    }

    public static Stream subClasses(OntClass clazz, boolean direct) {
        if (!clazz.canAsSuperClass()) {
            return Stream.empty();
        }
        if (direct) {
            Property reasonerProperty = reasonerProperty(clazz.getModel(), RDFS.subClassOf);
            if (reasonerProperty != null) {
                return explicitSubClasses(reasonerProperty, clazz).filter(x -> !clazz.equals(x));
            }
        }
        return HierarchySupport.treeNodes(
                clazz,
                it -> explicitSubClasses(RDFS.subClassOf, it),
                direct,
                OntGraphModelImpl.configValue(clazz.getModel(), OntModelControls.USE_BUILTIN_HIERARCHY_SUPPORT)
        );
    }

    public static Stream superClasses(OntClass clazz, boolean direct) {
        if (!clazz.canAsSubClass()) {
            return Stream.empty();
        }
        if (direct) {
            Property reasonerProperty = reasonerProperty(clazz.getModel(), RDFS.subClassOf);
            if (reasonerProperty != null) {
                return explicitSuperClasses(reasonerProperty, clazz).filter(x -> !clazz.equals(x));
            }
        }
        return HierarchySupport.treeNodes(
                clazz,
                it -> explicitSuperClasses(RDFS.subClassOf, it),
                direct,
                OntGraphModelImpl.configValue(clazz.getModel(), OntModelControls.USE_BUILTIN_HIERARCHY_SUPPORT)
        );
    }

    public static boolean hasSuperClass(OntClass clazz, OntClass candidateSuper, boolean direct) {
        if (clazz.equals(candidateSuper)) {
            // every class is a subclass of itself
            return true;
        }
        if (!clazz.canAsSubClass() || !candidateSuper.canAsSuperClass()) {
            return false;
        }
        if (direct) {
            Property reasonerProperty = reasonerProperty(clazz.getModel(), RDFS.subClassOf);
            if (reasonerProperty != null) {
                return clazz.getModel().contains(clazz, reasonerProperty, candidateSuper);
            }
        }
        return HierarchySupport.contains(
                clazz,
                candidateSuper,
                it -> explicitSuperClasses(RDFS.subClassOf, it),
                direct,
                OntGraphModelImpl.configValue(clazz.getModel(), OntModelControls.USE_BUILTIN_HIERARCHY_SUPPORT)
        );
    }

    static Stream explicitSuperClasses(Property predicate, OntObject clazz) {
        return clazz.objects(predicate, OntClass.class).filter(OntClass::canAsSuperClass).map(OntClass::asSuperClass);
    }

    static Stream explicitSubClasses(OntClass clazz) {
        return explicitSubClasses(RDFS.subClassOf, clazz);
    }

    static Stream explicitSubClasses(Property predicate, OntClass clazz) {
        return subjects(predicate, clazz, OntClass.class).filter(OntClass::canAsSubClass).map(OntClass::asSubClass);
    }

    @Override
    public Optional findRootStatement() {
        return getRequiredRootStatement(this, OWL2.Class);
    }

    @Override
    public abstract Class objectType();

    @Override
    public OntIndividual.Anonymous createIndividual() {
        return createAnonymousIndividual(getModel(), this);
    }

    @Override
    public OntIndividual.Named createIndividual(String uri) {
        return createNamedIndividual(getModel(), this, uri);
    }

    @Override
    public OntList createHasKey(Collection ope, Collection dpe) {
        Stream stream = Stream.concat(ope.stream(), dpe.stream());
        return createHasKey(getModel(), this, stream);
    }

    @Override
    public OntStatement addHasKeyStatement(OntRelationalProperty... properties) {
        return createHasKey(getModel(), this, Arrays.stream(properties)).getMainStatement();
    }

    @Override
    public Stream> hasKeys() {
        return listHasKeys(getModel(), this);
    }

    @Override
    public OntClassImpl removeHasKey(Resource list) throws OntJenaException.IllegalArgument {
        removeHasKey(getModel(), this, list);
        return this;
    }

    @Override
    public boolean isDisjoint(Resource candidate) {
        return isDisjoint(this, candidate);
    }

    @Override
    public Stream disjointClasses() {
        return disjointClasses(getModel(), this);
    }

    @Override
    public OntClass addDisjointClass(OntClass other) {
        addDisjoint(getModel(), this, other);
        return this;
    }

    @Override
    public OntStatement addDisjointWithStatement(OntClass other) {
        return addDisjointWithStatement(getModel(), this, other);
    }

    @Override
    public OntClass removeDisjointClass(Resource other) {
        removeDisjoint(getModel(), this, other);
        return this;
    }

    @Override
    public Stream equivalentClasses() {
        return equivalentClasses(getModel(), this);
    }

    @Override
    public boolean hasSuperClass(OntClass clazz, boolean direct) {
        return OntClassImpl.hasSuperClass(this, clazz, direct);
    }

    @Override
    public OntStatement addEquivalentClassStatement(OntClass other) {
        return addEquivalentClass(getModel(), this, other);
    }

    @Override
    public OntClass removeEquivalentClass(Resource other) {
        removeEquivalentClass(getModel(), this, other);
        return this;
    }

    @Override
    public Stream superClasses(boolean direct) {
        return superClasses(this, direct);
    }

    @Override
    public Stream subClasses(boolean direct) {
        return subClasses(this, direct);
    }

    @Override
    public Stream individuals(boolean direct) {
        return individuals(this, direct);
    }

    @Override
    public boolean hasDeclaredProperty(OntProperty property, boolean direct) {
        return testDomain(this, property, direct);
    }

    @Override
    public Stream declaredProperties(boolean direct) {
        return declaredProperties(this, direct);
    }

    @Override
    public boolean isHierarchyRoot() {
        return isHierarchyRoot(this);
    }

    public enum CardinalityType {
        EXACTLY(OWL2.qualifiedCardinality, OWL2.cardinality),
        MAX(OWL2.maxQualifiedCardinality, OWL2.maxCardinality),
        MIN(OWL2.minQualifiedCardinality, OWL2.minCardinality);
        static final RDFDatatype NON_NEGATIVE_INTEGER = XSDDatatype.XSDnonNegativeInteger;
        static final Node CLASS_REFERENCE = OWL2.onClass.asNode();
        static final Node RANGE_REFERENCE = OWL2.onDataRange.asNode();
        private final Property qualifiedPredicate, predicate;

        CardinalityType(Property qualifiedPredicate, Property predicate) {
            this.qualifiedPredicate = qualifiedPredicate;
            this.predicate = predicate;
        }

        private static boolean isObjectOfType(EnhGraph g, Node n, Class type) {
            return OntEnhGraph.canAs(type, n, g);
        }

        public static boolean isNonNegativeInteger(Node n) {
            return n.isLiteral() && NON_NEGATIVE_INTEGER.equals(n.getLiteralDatatype());
        }

        public Property getPredicate(boolean isQualified) {
            return isQualified ? qualifiedPredicate : predicate;
        }

        public boolean isQualified(Node s, EnhGraph g, Class objectType) {
            if (!hasCardinality(s, qualifiedPredicate, g)) {
                return false;
            }
            Node p;
            if (objectType == OntClass.class) {
                p = CLASS_REFERENCE;
            } else if (objectType == OntDataRange.class) {
                p = RANGE_REFERENCE;
            } else {
                return false;
            }
            return Iterators.findFirst(g.asGraph().find(s, p, Node.ANY)
                    .filterKeep(t -> isObjectOfType(g, t.getObject(), objectType))).isPresent();
        }

        public boolean isNonQualified(Node s, EnhGraph g) {
            return hasCardinality(s, predicate, g);
        }

        private boolean hasCardinality(Node s, Property p, EnhGraph g) {
            return Iterators.findFirst(g.asGraph().find(s, p.asNode(), Node.ANY)
                    .filterKeep(t -> isNonNegativeInteger(t.getObject()))).isPresent();
        }
    }

    public static class QLObjectSomeValuesFromImpl extends ObjectSomeValuesFromImpl {
        public QLObjectSomeValuesFromImpl(Node n, EnhGraph m) {
            super(n, m);
        }

        @Override
        public OntClass asSubClass() {
            if (OWL2.Thing.equals(getValue())) {
                return this;
            }
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a subclass");
        }

        @Override
        public boolean canAsSubClass() {
            return OWL2.Thing.equals(getValue());
        }

        @Override
        public OntClass asSuperClass() {
            if (getValue().isURIResource()) {
                return this;
            }
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a superclass");
        }

        @Override
        public boolean canAsSuperClass() {
            return getValue().isURIResource();
        }

        @Override
        public OntClass asEquivalentClass() {
            if (OWL2.Thing.equals(getValue())) {
                return this;
            }
            throw new OntJenaException.Unsupported("Specification does not allow this class to be an equivalent class");
        }

        @Override
        public boolean canAsEquivalentClass() {
            return OWL2.Thing.equals(getValue());
        }

        @Override
        public OntClass asDisjointClass() {
            if (OWL2.Thing.equals(getValue())) {
                return this;
            }
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a disjoint class");
        }

        @Override
        public boolean canAsDisjointClass() {
            return OWL2.Thing.equals(getValue());
        }

        @Override
        public OntClass asAssertionClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a object position of class assertion");
        }

        @Override
        public boolean canAsAssertionClass() {
            return false;
        }
    }

    public static class RLObjectSomeValuesFromImpl extends ObjectSomeValuesFromImpl {
        public RLObjectSomeValuesFromImpl(Node n, EnhGraph m) {
            super(n, m);
        }

        @Override
        public OntClass asSuperClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a superclass");
        }

        @Override
        public boolean canAsSuperClass() {
            return false;
        }

        @Override
        public OntClass asAssertionClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a type of individual");
        }

        @Override
        public boolean canAsAssertionClass() {
            return false;
        }

        @Override
        public OntClass asEquivalentClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be an equivalent");
        }

        @Override
        public boolean canAsEquivalentClass() {
            return false;
        }
    }

    public static class ObjectSomeValuesFromImpl
            extends ComponentRestrictionImpl implements ObjectSomeValuesFrom {
        public ObjectSomeValuesFromImpl(Node n, EnhGraph m) {
            super(n, m, OWL2.someValuesFrom, OntClass.class, OntObjectProperty.class);
        }

        @Override
        public Class objectType() {
            return ObjectSomeValuesFrom.class;
        }
    }

    public static class QLDataSomeValuesFromImpl extends DataSomeValuesFromImpl {
        public QLDataSomeValuesFromImpl(Node n, EnhGraph m) {
            super(n, m);
        }

        @Override
        public OntClass asAssertionClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a object position of class assertion");
        }

        @Override
        public boolean canAsAssertionClass() {
            return false;
        }
    }

    public static class RLDataSomeValuesFromImpl extends DataSomeValuesFromImpl {

        public RLDataSomeValuesFromImpl(Node n, EnhGraph m) {
            super(n, m);
        }

        @Override
        public OntClass asSuperClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a superclass");
        }

        @Override
        public boolean canAsSuperClass() {
            return false;
        }

        @Override
        public OntClass asAssertionClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a type of individual");
        }

        @Override
        public boolean canAsAssertionClass() {
            return false;
        }

        @Override
        public OntClass asEquivalentClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be an equivalent class");
        }

        @Override
        public boolean canAsEquivalentClass() {
            return false;
        }

    }

    public static class DataSomeValuesFromImpl
            extends ComponentRestrictionImpl implements DataSomeValuesFrom {
        public DataSomeValuesFromImpl(Node n, EnhGraph m) {
            super(n, m, OWL2.someValuesFrom, OntDataRange.class, OntDataProperty.class);
        }

        @Override
        public Class objectType() {
            return DataSomeValuesFrom.class;
        }
    }

    public static class RLObjectAllValuesFromImpl extends ObjectAllValuesFromImpl {

        public RLObjectAllValuesFromImpl(Node n, EnhGraph m) {
            super(n, m);
        }

        @Override
        public OntClass asSubClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a subclass");
        }

        @Override
        public boolean canAsSubClass() {
            return false;
        }

        @Override
        public OntClass asEquivalentClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be an equivalent class");
        }

        @Override
        public boolean canAsEquivalentClass() {
            return false;
        }

        @Override
        public OntClass asDisjointClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a disjoint class");
        }

        @Override
        public boolean canAsDisjointClass() {
            return false;
        }
    }

    public static class ObjectAllValuesFromImpl
            extends ComponentRestrictionImpl implements ObjectAllValuesFrom {
        public ObjectAllValuesFromImpl(Node n, EnhGraph m) {
            super(n, m, OWL2.allValuesFrom, OntClass.class, OntObjectProperty.class);
        }

        @Override
        public Class objectType() {
            return ObjectAllValuesFrom.class;
        }
    }

    public static class RLDataAllValuesFromImpl extends DataAllValuesFromImpl {

        public RLDataAllValuesFromImpl(Node n, EnhGraph m) {
            super(n, m);
        }

        @Override
        public OntClass asSubClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a subclass");
        }

        @Override
        public boolean canAsSubClass() {
            return false;
        }

        @Override
        public OntClass asEquivalentClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be an equivalent class");
        }

        @Override
        public boolean canAsEquivalentClass() {
            return false;
        }

        @Override
        public OntClass asDisjointClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a disjoint class");
        }

        @Override
        public boolean canAsDisjointClass() {
            return false;
        }
    }

    public static class DataAllValuesFromImpl
            extends ComponentRestrictionImpl implements DataAllValuesFrom {
        public DataAllValuesFromImpl(Node n, EnhGraph m) {
            super(n, m, OWL2.allValuesFrom, OntDataRange.class, OntDataProperty.class);
        }

        @Override
        public Class objectType() {
            return DataAllValuesFrom.class;
        }
    }

    public static class ObjectHasValueImpl
            extends ComponentRestrictionImpl implements ObjectHasValue {
        public ObjectHasValueImpl(Node n, EnhGraph m) {
            super(n, m, OWL2.hasValue, OntIndividual.class, OntObjectProperty.class);
        }

        @Override
        public Class objectType() {
            return ObjectHasValue.class;
        }
    }

    public static class DataHasValueImpl
            extends ComponentRestrictionImpl implements DataHasValue {
        public DataHasValueImpl(Node n, EnhGraph m) {
            super(n, m, OWL2.hasValue, Literal.class, OntDataProperty.class);
        }

        @Override
        public Class objectType() {
            return DataHasValue.class;
        }
    }

    public static class RLUnionOfImpl extends UnionOfImpl {
        public RLUnionOfImpl(Node n, EnhGraph m) {
            super(n, m);
        }

        @Override
        public OntClass asSuperClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a superclass");
        }

        @Override
        public boolean canAsSuperClass() {
            return false;
        }

        @Override
        public OntClass asEquivalentClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be an equivalent class");
        }

        @Override
        public boolean canAsEquivalentClass() {
            return false;
        }

        @Override
        public Stream components() {
            return getList().members().filter(OntClass::canAsSubClass);
        }
    }

    public static class UnionOfImpl extends CollectionOfImpl implements UnionOf {
        public UnionOfImpl(Node n, EnhGraph m) {
            super(n, m, OWL2.unionOf, OntClass.class);
        }

        @Override
        public Class objectType() {
            return UnionOf.class;
        }
    }

    public static class QLIntersectionOfImpl extends IntersectionOfImpl {
        public QLIntersectionOfImpl(Node n, EnhGraph m) {
            super(n, m);
        }

        @Override
        public OntClass asSubClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a subclass");
        }

        @Override
        public boolean canAsSubClass() {
            return false;
        }

        @Override
        public OntClass asAssertionClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a object position of class assertion");
        }

        @Override
        public boolean canAsAssertionClass() {
            return false;
        }

        @Override
        public OntClass asEquivalentClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be an equivalent class");
        }

        @Override
        public boolean canAsEquivalentClass() {
            return false;
        }

        @Override
        public OntClass asDisjointClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a disjoint class");
        }

        @Override
        public boolean canAsDisjointClass() {
            return false;
        }

        @Override
        public Stream components() {
            return getList().members().filter(OntClass::canAsSuperClass).map(OntClass::asSuperClass);
        }
    }

    /**
     * @see RL: Class Expressions
     */
    public static class RLIntersectionOfImpl extends IntersectionOfImpl {
        public RLIntersectionOfImpl(Node n, EnhGraph m) {
            super(n, m);
        }

        @Override
        public OntClass asSubClass() {
            return asSubClass(() -> "Specification does not allow this class to be a subclass");
        }

        @Override
        public boolean canAsSubClass() {
            return hasMemberSubClasses();
        }

        @Override
        public OntClass asSuperClass() {
            return asSuperClass(() -> "Specification does not allow this class to be a superclass");
        }

        @Override
        public boolean canAsSuperClass() {
            return hasMemberSuperClasses();
        }

        @Override
        public OntClass asEquivalentClass() {
            Collection res = collectEquivalentClasses();
            if (res.isEmpty()) {
                throw new OntJenaException.Unsupported("Specification does not allow this class to be an equivalent class");
            }
            return new RLIntersectionOfImpl(this.node, this.enhGraph) {
                @Override
                public Stream components() {
                    return res.stream();
                }
            };
        }

        @Override
        public boolean canAsEquivalentClass() {
            return hasMemberEquivalentClasses();
        }

        @Override
        public OntClass asAssertionClass() {
            return asSuperClass(() -> "Specification does not allow this class to be a type of individual");
        }

        @Override
        public boolean canAsAssertionClass() {
            return hasMemberSuperClasses();
        }

        @Override
        public OntClass asDisjointClass() {
            return asSubClass(() -> "Specification does not allow this class to be a disjoint class");
        }

        @Override
        public boolean canAsDisjointClass() {
            return hasMemberSubClasses();
        }

        private OntClass asSuperClass(Supplier message) {
            Collection res = collectMemberSuperClasses();
            if (res.isEmpty()) {
                throw new OntJenaException.Unsupported(message.get());
            }
            return new RLIntersectionOfImpl(this.node, this.enhGraph) {
                @Override
                public Stream components() {
                    return res.stream();
                }
            };
        }

        private OntClass asSubClass(Supplier message) {
            Collection res = collectMemberSubClasses();
            if (res.isEmpty()) {
                throw new OntJenaException.Unsupported(message.get());
            }
            return new RLIntersectionOfImpl(this.node, this.enhGraph) {
                @Override
                public Stream components() {
                    return res.stream();
                }
            };
        }

        private Collection collectMemberSubClasses() {
            Set res = new LinkedHashSet<>();
            getList().members().forEach(it -> {
                if (it.canAsSubClass()) {
                    res.add(it.asSubClass());
                }
            });
            return res.size() > 1 ? res : List.of();
        }

        private Collection collectMemberSuperClasses() {
            Set res = new LinkedHashSet<>();
            getList().members().forEach(it -> {
                if (it.canAsSuperClass()) {
                    res.add(it.asSuperClass());
                }
            });
            return res.size() > 1 ? res : List.of();
        }

        private Collection collectEquivalentClasses() {
            Set res = new LinkedHashSet<>();
            getList().members().forEach(it -> {
                if (it.canAsEquivalentClass()) {
                    res.add(it.asEquivalentClass());
                }
            });
            return res.size() > 1 ? res : List.of();
        }

        private boolean hasMemberSubClasses() {
            try (Stream members = getList().members().filter(OntClass::canAsSubClass)) {
                return Iterators.hasAtLeast(members.iterator(), 2);
            }
        }

        private boolean hasMemberSuperClasses() {
            try (Stream members = getList().members().filter(OntClass::canAsSuperClass)) {
                return Iterators.hasAtLeast(members.iterator(), 2);
            }
        }

        private boolean hasMemberEquivalentClasses() {
            try (Stream members = getList().members().filter(OntClass::canAsEquivalentClass)) {
                return Iterators.hasAtLeast(members.iterator(), 2);
            }
        }
    }

    public static class IntersectionOfImpl extends CollectionOfImpl implements IntersectionOf {
        public IntersectionOfImpl(Node n, EnhGraph m) {
            super(n, m, OWL2.intersectionOf, OntClass.class);
        }

        @Override
        public Class objectType() {
            return IntersectionOf.class;
        }
    }

    public static class RLOneOfImpl extends OneOfImpl {
        public RLOneOfImpl(Node n, EnhGraph m) {
            super(n, m);
        }

        @Override
        public OntClass asSuperClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a superclass");
        }

        @Override
        public boolean canAsSuperClass() {
            return false;
        }

        @Override
        public OntClass asAssertionClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a type of individual");
        }

        @Override
        public boolean canAsAssertionClass() {
            return false;
        }

        @Override
        public OntClass asEquivalentClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be an equivalent class");
        }

        @Override
        public boolean canAsEquivalentClass() {
            return false;
        }
    }

    public static class OneOfImpl extends CollectionOfImpl implements OneOf {
        public OneOfImpl(Node n, EnhGraph m) {
            super(n, m, OWL2.oneOf, OntIndividual.class);
        }

        @Override
        public Class objectType() {
            return OneOf.class;
        }
    }

    public static class DataMinCardinalityImpl
            extends CardinalityRestrictionImpl implements DataMinCardinality {
        public DataMinCardinalityImpl(Node n, EnhGraph m) {
            super(n, m, OWL2.onDataRange, OntDataRange.class, OntDataProperty.class, CardinalityType.MIN);
        }

        @Override
        public Class objectType() {
            return DataMinCardinality.class;
        }
    }

    public static class ObjectMinCardinalityImpl
            extends CardinalityRestrictionImpl implements ObjectMinCardinality {
        public ObjectMinCardinalityImpl(Node n, EnhGraph m) {
            super(n, m, OWL2.onClass, OntClass.class, OntObjectProperty.class, CardinalityType.MIN);
        }

        @Override
        public Class objectType() {
            return ObjectMinCardinality.class;
        }
    }

    public static class RLDataMaxCardinalityImpl extends DataMaxCardinalityImpl {

        public RLDataMaxCardinalityImpl(Node n, EnhGraph m) {
            super(n, m);
        }

        @Override
        public OntClass asSubClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a subclass");
        }

        @Override
        public boolean canAsSubClass() {
            return false;
        }

        @Override
        public OntClass asEquivalentClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be an equivalent class");
        }

        @Override
        public boolean canAsEquivalentClass() {
            return false;
        }

        @Override
        public OntClass asDisjointClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a disjoint class");
        }

        @Override
        public boolean canAsDisjointClass() {
            return false;
        }
    }

    public static class DataMaxCardinalityImpl
            extends CardinalityRestrictionImpl implements DataMaxCardinality {
        public DataMaxCardinalityImpl(Node n, EnhGraph m) {
            super(n, m, OWL2.onDataRange, OntDataRange.class, OntDataProperty.class, CardinalityType.MAX);
        }

        @Override
        public Class objectType() {
            return DataMaxCardinality.class;
        }
    }

    public static class RLObjectMaxCardinalityImpl extends ObjectMaxCardinalityImpl {

        public RLObjectMaxCardinalityImpl(Node n, EnhGraph m) {
            super(n, m);
        }

        @Override
        public OntClass asSubClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a subclass");
        }

        @Override
        public boolean canAsSubClass() {
            return false;
        }

        @Override
        public OntClass asEquivalentClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be an equivalent class");
        }

        @Override
        public boolean canAsEquivalentClass() {
            return false;
        }

        @Override
        public OntClass asDisjointClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a disjoint class");
        }

        @Override
        public boolean canAsDisjointClass() {
            return false;
        }
    }

    public static class ObjectMaxCardinalityImpl
            extends CardinalityRestrictionImpl implements ObjectMaxCardinality {
        public ObjectMaxCardinalityImpl(Node n, EnhGraph m) {
            super(n, m, OWL2.onClass, OntClass.class, OntObjectProperty.class, CardinalityType.MAX);
        }

        @Override
        public Class objectType() {
            return ObjectMaxCardinality.class;
        }
    }

    public static class DataCardinalityImpl
            extends CardinalityRestrictionImpl implements DataCardinality {
        public DataCardinalityImpl(Node n, EnhGraph m) {
            super(n, m, OWL2.onDataRange, OntDataRange.class, OntDataProperty.class, CardinalityType.EXACTLY);
        }

        @Override
        public Class objectType() {
            return DataCardinality.class;
        }
    }

    public static class ObjectCardinalityImpl
            extends CardinalityRestrictionImpl implements ObjectCardinality {
        public ObjectCardinalityImpl(Node n, EnhGraph m) {
            super(n, m, OWL2.onClass, OntClass.class, OntObjectProperty.class, CardinalityType.EXACTLY);
        }

        @Override
        public Class objectType() {
            return ObjectCardinality.class;
        }
    }

    public static class HasSelfImpl extends OnPropertyRestrictionImpl implements HasSelf {
        public HasSelfImpl(Node n, EnhGraph m) {
            super(n, m, OntObjectProperty.class);
        }

        @Override
        public ExtendedIterator listSpec() {
            return Iterators.concat(super.listSpec(), listRequired(OWL2.hasSelf));
        }

        @Override
        public Class objectType() {
            return HasSelf.class;
        }
    }

    public static class QLComplementOfImpl extends ComplementOfImpl {
        public QLComplementOfImpl(Node n, EnhGraph m) {
            super(n, m);
        }

        @Override
        public OntClass asSubClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a subclass");
        }

        @Override
        public boolean canAsSubClass() {
            return false;
        }

        @Override
        public boolean canAsSuperClass() {
            return getValue().canAsSubClass();
        }

        @Override
        public OntClass asAssertionClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be an individual type");
        }

        @Override
        public boolean canAsAssertionClass() {
            return false;
        }
    }

    public static class RLComplementOfImpl extends ComplementOfImpl {
        public RLComplementOfImpl(Node n, EnhGraph m) {
            super(n, m);
        }

        @Override
        public OntClass asSubClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a subclass");
        }

        @Override
        public boolean canAsSubClass() {
            return false;
        }

        @Override
        public OntClass asSuperClass() {
            if (getValue().canAsSubClass()) {
                return this;
            }
            throw new OntJenaException.Unsupported("Specification does not allow this class to be an super class");
        }

        @Override
        public boolean canAsSuperClass() {
            return getValue().canAsSubClass();
        }

        @Override
        public OntClass asAssertionClass() {
            if (getValue().canAsSubClass()) {
                return this;
            }
            throw new OntJenaException.Unsupported("Specification does not allow this class to be an individual type");
        }

        @Override
        public boolean canAsAssertionClass() {
            return getValue().canAsSubClass();
        }

        @Override
        public OntClass asEquivalentClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be an equivalent class");
        }

        @Override
        public boolean canAsEquivalentClass() {
            return false;
        }

        @Override
        public OntClass asDisjointClass() {
            throw new OntJenaException.Unsupported("Specification does not allow this class to be a disjoint class");
        }

        @Override
        public boolean canAsDisjointClass() {
            return false;
        }
    }

    public static class ComplementOfImpl extends OntClassImpl implements ComplementOf {
        public ComplementOfImpl(Node n, EnhGraph m) {
            super(n, m);
        }

        @Override
        public ExtendedIterator listSpec() {
            return Iterators.concat(super.listSpec(), listRequired(OWL2.complementOf));
        }

        @Override
        public Class objectType() {
            return ComplementOf.class;
        }

        @Override
        public OntClass getValue() {
            return getRequiredObject(OWL2.complementOf, OntClass.class);
        }

        @Override
        public ComplementOfImpl setValue(OntClass c) {
            Objects.requireNonNull(c, "Null component");
            clear();
            addProperty(OWL2.complementOf, c);
            return this;
        }

        void clear() {
            removeAll(OWL2.complementOf);
        }
    }

    public static class NaryDataAllValuesFromImpl
            extends NaryRestrictionImpl implements NaryDataAllValuesFrom {

        public NaryDataAllValuesFromImpl(Node n, EnhGraph m) {
            super(n, m, OWL2.allValuesFrom, OntDataRange.class, OntDataProperty.class);
        }

        @Override
        public Class objectType() {
            return NaryDataAllValuesFrom.class;
        }
    }

    public static class NaryDataSomeValuesFromImpl
            extends NaryRestrictionImpl implements NaryDataSomeValuesFrom {

        public NaryDataSomeValuesFromImpl(Node n, EnhGraph m) {
            super(n, m, OWL2.someValuesFrom, OntDataRange.class, OntDataProperty.class);
        }

        @Override
        public Class objectType() {
            return NaryDataSomeValuesFrom.class;
        }
    }

    /**
     * An abstract superclass for {@link IntersectionOf}, {@link OneOf}, {@link UnionOf}.
     *
     * @param  {@link OntObject}
     */
    protected static abstract class CollectionOfImpl
            extends OntClassImpl implements CollectionOf {
        protected final Property predicate;
        protected final Class type;

        protected CollectionOfImpl(Node n, EnhGraph m, Property predicate, Class type) {
            super(n, m);
            this.predicate = OntJenaException.notNull(predicate, "Null predicate.");
            this.type = OntJenaException.notNull(type, "Null view.");
        }

        @Override
        public ExtendedIterator listSpec() {
            return Iterators.concat(super.listSpec(), getList().listContent());
        }

        @Override
        public OntListImpl getList() {
            return getModel().asOntList(getRequiredObject(predicate, RDFList.class), this, predicate, true, null, type);
        }
    }

    /**
     * Base for all {@link Restriction} impls.
     */
    @SuppressWarnings("javadoc")
    public static class RestrictionImpl extends OntClassImpl implements Restriction {

        public RestrictionImpl(Node n, EnhGraph m) {
            super(n, m);
        }

        @Override
        public Optional findRootStatement() {
            return getRequiredRootStatement(this, OWL2.Restriction);
        }

        @Override
        public Class objectType() {
            return OntClass.Restriction.class;
        }
    }

    /**
     * Abstract implementation for any restriction with {@code owl:onProperty} predicate.
     * Can be used instantiate directly if config settings allow.
     *
     * @param 

a subtype of {@link OntRelationalProperty Data or Object Property Expression} * @param return type for {@link OWL2#onProperty} setter */ public static class OnPropertyRestrictionImpl

extends RestrictionImpl implements UnaryRestriction

{ protected final Class

propertyView; /** * @param n {@link Node} * @param m {@link EnhGraph} * @param propertyType Class-type for {@link OntRelationalProperty} */ public OnPropertyRestrictionImpl(Node n, EnhGraph m, Class

propertyType) { super(n, m); this.propertyView = propertyType; } @Override public Class objectType() { return OntClass.UnaryRestriction.class; } @Override public P getProperty() { return getRequiredObject(OWL2.onProperty, propertyView); } @SuppressWarnings("unchecked") public R setProperty(P property) { Objects.requireNonNull(property, "Null " + OntEnhNodeFactories.viewAsString(propertyView)); removeAll(OWL2.onProperty).addProperty(OWL2.onProperty, property); return (R) this; } @Override public ExtendedIterator listSpec() { return Iterators.concat(super.listSpec(), listRequired(OWL2.onProperty)); } } /** * Abstract base component-restriction class. * It's for CE which has owl:onProperty and some component also * (with predicate owl:dataRange, owl:onClass, owl:someValuesFrom, owl:allValuesFrom) * * @param a class-type of {@link RDFNode rdf-node} * @param

a class-type of {@link OntRelationalProperty data or object property-expression} * @param a subtype of {@link ComponentRestrictionImpl} */ public static abstract class ComponentRestrictionImpl> extends OnPropertyRestrictionImpl implements ComponentRestriction { protected final Property predicate; protected final Class objectView; /** * @param n Node * @param m EnhGraph * @param predicate predicate for value * @param objectView Class * @param propertyView Class */ protected ComponentRestrictionImpl(Node n, EnhGraph m, Property predicate, Class objectView, Class

propertyView) { super(n, m, propertyView); this.predicate = OntJenaException.notNull(predicate, "Null predicate."); this.objectView = OntJenaException.notNull(objectView, "Null object view."); } @Override public ExtendedIterator listSpec() { return listSpec(true); } protected ExtendedIterator listSpec(boolean requireObject) { return requireObject ? Iterators.concat(super.listSpec(), listRequired(predicate)) : super.listSpec(); } @Override public O getValue() { return getModel().getNodeAs(getRequiredProperty(predicate).getObject().asNode(), objectView); } @SuppressWarnings("unchecked") public R setValue(O c) { Objects.requireNonNull(c, "Null filler"); removeAll(predicate).addProperty(predicate, c); return (R) this; } } /** * Abstraction for any cardinality restriction. * * @param either {@link OntClass} (predicate {@link OWL2#onClass owl:onClass}) or {@link OntDataRange} * (predicate: {@link OWL2#onDataRange owl:onDataRange}) * @param

either {@link OntObjectProperty} or {@link OntDataProperty} * @param subtype of {@link CardinalityRestrictionImpl} */ public static abstract class CardinalityRestrictionImpl> extends ComponentRestrictionImpl implements CardinalityRestriction { protected final CardinalityType cardinalityType; /** * @param n {@link Node} * @param m {@link EnhGraph} * @param predicate either {@code owl:onDataRange} or {@code owl:onClass} * @param objectView interface of class expression or data range * @param propertyView interface, property expression * @param cardinalityType type of cardinality. */ protected CardinalityRestrictionImpl(Node n, EnhGraph m, Property predicate, Class objectView, Class

propertyView, CardinalityType cardinalityType) { super(n, m, predicate, objectView, propertyView); this.cardinalityType = cardinalityType; } @Override public ExtendedIterator listSpec() { // note: object value is null for non-qualified restrictions. boolean q; return Iterators.concat(super.listSpec(q = isQualified()), listRequired(getCardinalityPredicate(q))); } @Override public O getValue() { // null for non-qualified restrictions: return object(predicate, objectView).orElseGet(this::getUnqualifiedValue); } @SuppressWarnings("unchecked") @Override public R setValue(O value) { Literal c = getCardinalityLiteral(); removeAll(predicate); if (!isQualified(value)) { // null, owl:Thing, rdfs:Label removeAll(getCardinalityPredicate(true)) .addProperty(getCardinalityPredicate(false), c); } else { removeAll(getCardinalityPredicate(false)) .addProperty(getCardinalityPredicate(true), c) .addProperty(predicate, value); } return (R) this; } @SuppressWarnings("unchecked") private O getUnqualifiedValue() { OntModel m = getModel(); OntObject res = OntClass.class.isAssignableFrom(objectView) ? m.getOWLThing() : m.getRDFSLiteral(); return (O) res; } @Override public int getCardinality() { return getCardinalityLiteral().getInt(); } @SuppressWarnings("unchecked") public R setCardinality(int cardinality) { Literal value = createNonNegativeIntegerLiteral(cardinality); Property property = getCardinalityPredicate(); removeAll(property).addLiteral(property, value); return (R) this; } private Literal getCardinalityLiteral() { return getRequiredObject(getCardinalityPredicate(), Literal.class); } protected Property getCardinalityPredicate() { return getCardinalityPredicate(isQualified()); } private Property getCardinalityPredicate(boolean q) { return cardinalityType.getPredicate(q); } @Override public boolean isQualified() { return isQualified(getValue()) && hasProperty(cardinalityType.getPredicate(true)); } } public static abstract class NaryRestrictionImpl> extends OntClassImpl implements NaryRestriction { protected final Property predicate; protected final Class objectType; // always OntDR protected final Class

propertyType; protected NaryRestrictionImpl(Node n, EnhGraph m, Property predicate, Class objectType, Class

propertyType) { super(n, m); this.predicate = predicate; this.objectType = objectType; this.propertyType = propertyType; } public static void validateArity(OntDataRange dr, Collection properties) { properties.forEach(x -> OntJenaException.notNull(x, "Null data property")); if (dr.arity() == properties.size()) return; throw new OntJenaException.IllegalArgument("The number of data properties (" + properties.size() + ") " + "must be equal to the data range arity (" + dr.arity() + ")."); } @Override public Optional findRootStatement() { return getRequiredRootStatement(this, OWL2.Restriction); } @Override public O getValue() { return getRequiredObject(predicate, objectType); } @SuppressWarnings("unchecked") public R setValue(O value) { Objects.requireNonNull(value); removeAll(predicate).addProperty(predicate, value); return (R) this; } @SuppressWarnings("unchecked") public R setComponents(Collection

properties) { validateArity((OntDataRange) getValue(), (Collection) properties); getList().clear().addAll(properties); return (R) this; } @Override public Class objectType() { return NaryRestriction.class; } @Override public ExtendedIterator listSpec() { return Iterators.concat(super.listSpec(), listRequired(predicate), getList().listContent()); } @Override public OntListImpl

getList() { return getModel().asOntList(getRequiredObject(OWL2.onProperties, RDFList.class), this, OWL2.onProperties, propertyType); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy