package org.apache.rya.reasoning;

/*
 * 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.
 */

import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.vocabulary.FOAF;
import org.eclipse.rdf4j.model.vocabulary.OWL;
import org.eclipse.rdf4j.model.vocabulary.RDFS;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

/**
 * Test the application of the OWL RL/RDF schema ("scm-") rules.
 */
public class SchemaReasoningTest {
    private Schema schema;

    /**
     * Setup: load in test data
     */
    @Before
    public void insertData() {
        schema = new Schema();
        // Lifted from the LUBM ontology:
        schema.processTriple(TestUtils.statement(TestUtils.uri("Dean"),
            RDFS.SUBCLASSOF, TestUtils.uri("Professor")));
        schema.processTriple(TestUtils.statement(TestUtils.uri("Professor"),
            RDFS.SUBCLASSOF, TestUtils.uri("Faculty")));
        schema.processTriple(TestUtils.statement(TestUtils.uri("Faculty"),
            RDFS.SUBCLASSOF, TestUtils.uri("Employee")));
        schema.processTriple(TestUtils.statement(TestUtils.uri("University"),
            RDFS.SUBCLASSOF, TestUtils.uri("Organization")));
        schema.processTriple(TestUtils.statement(TestUtils.uri("ResearchGroup"),
            RDFS.SUBCLASSOF, TestUtils.uri("Organization")));
        schema.processTriple(TestUtils.statement(TestUtils.uri("headOf"),
            RDFS.SUBPROPERTYOF, TestUtils.uri("worksFor")));
        schema.processTriple(TestUtils.statement(TestUtils.uri("worksFor"),
            RDFS.SUBPROPERTYOF, TestUtils.uri("memberOf")));
        schema.processTriple(TestUtils.statement(TestUtils.uri("advisor"),
            RDFS.RANGE, TestUtils.uri("Professor")));
        schema.processTriple(TestUtils.statement(TestUtils.uri("hasAlumnus"),
            RDFS.DOMAIN, TestUtils.uri("University")));
        // Not explicitly part of LUBM but helpful for a couple tests:
        schema.processTriple(TestUtils.statement(TestUtils.uri("memberOf"),
            RDFS.DOMAIN, TestUtils.uri("Person")));
        schema.processTriple(TestUtils.statement(TestUtils.uri("memberOf"),
            RDFS.RANGE, TestUtils.uri("Organization")));
        schema.processTriple(TestUtils.statement(TestUtils.uri("researchSpecialty"),
            RDFS.SUBPROPERTYOF, TestUtils.uri("researchInterest")));
        schema.closure();
    }

    /**
     * scm-cls
     */
    @Test
    public void testSelfSubclass() throws Exception {
        Assert.assertTrue("Any class should be its own superclass",
            schema.getClass(TestUtils.uri("foo")).getSuperClasses().contains(
            TestUtils.uri("foo")));
    }
    @Test
    public void testSelfEquivalentClass() throws Exception {
        Assert.assertTrue("Any class should be its own equivalent",
            schema.getClass(TestUtils.uri("foo")).getEquivalentClasses().contains(
            TestUtils.uri("foo")));
    }
    @Test
    public void testSubclassOwlThing() throws Exception {
        Assert.assertTrue("Any class should be a subclass of owl:Thing",
            schema.getClass(TestUtils.uri("foo")).getSuperClasses().contains(
            OWL.THING));
    }

    /**
     * scm-sco
     */
    @Test
    public void testSubclassTransitivity() throws Exception {
        Assert.assertTrue("Failed to infer indirect superclass",
            schema.getClass(TestUtils.uri("Dean")).getSuperClasses().contains(
            TestUtils.uri("Employee")));
    }

    /**
     * scm-eqc1
     */
    @Test
    public void testEquivalentImpliesSubclass() throws Exception {
        schema.processTriple(TestUtils.statement(TestUtils.uri("Person"),
            OWL.EQUIVALENTCLASS, FOAF.PERSON));
        Assert.assertTrue("Failed to infer subClassOf from equivalentClass",
            schema.getClass(TestUtils.uri("Person")).getSuperClasses().contains(
            FOAF.PERSON));
    }

    /**
     * scm-eqc2
     */
    @Test
    public void testSubclassImpliesEquivalence() throws Exception {
        schema.processTriple(TestUtils.statement(FOAF.PERSON,
            RDFS.SUBCLASSOF,
            TestUtils.uri("Person")));
        schema.processTriple(TestUtils.statement(TestUtils.uri("Person"),
            RDFS.SUBCLASSOF,
            FOAF.PERSON));
        Assert.assertTrue("Failed to infer equivalentClass from subClassOf",
            schema.getClass(FOAF.PERSON).getEquivalentClasses().contains(
            TestUtils.uri("Person")));
    }

    /**
     * scm-op, scm-dp
     */
    @Test
    public void testSelfSubproperty() throws Exception {
        Assert.assertTrue("Any property should be its own superproperty",
            schema.getProperty(TestUtils.uri("foo")).getSuperProperties().contains(
            TestUtils.uri("foo")));
    }
    @Test
    public void testSelfEquivalentProperty() throws Exception {
        Assert.assertTrue("Any property should be its own equivalent",
            schema.getProperty(TestUtils.uri("foo")).getEquivalentProperties().contains(
            TestUtils.uri("foo")));
    }

    /**
     * scm-spo
     */
    @Test
    public void testSubpropertyTransitivity() throws Exception {
        Assert.assertTrue("Failed to infer indirect superproperty",
            schema.getProperty(TestUtils.uri("headOf")).getSuperProperties().contains(
            TestUtils.uri("memberOf")));
    }

    /**
     * scm-eqp1
     */
    @Test
    public void testEquivalentImpliesSubproperty() throws Exception {
        schema.processTriple(TestUtils.statement(TestUtils.uri("memberOf"),
            OWL.EQUIVALENTPROPERTY, TestUtils.uri("test:memberOf")));
        Assert.assertTrue("Failed to infer subPropertyOf from equivalentProperty",
            schema.getProperty(TestUtils.uri("test:memberOf")).getSuperProperties().contains(
            TestUtils.uri("memberOf")));
    }

    /**
     * scm-eqp2
     */
    @Test
    public void testSubpropertyImpliesEquivalence() throws Exception {
        schema.processTriple(TestUtils.statement(TestUtils.uri("advisor"),
            RDFS.SUBPROPERTYOF, TestUtils.uri("test:advisor")));
        schema.processTriple(TestUtils.statement(TestUtils.uri("test:advisor"),
            RDFS.SUBPROPERTYOF, TestUtils.uri("advisor")));
        Assert.assertTrue("Failed to infer equivalentProperty from mutual "
            + "subPropertyOf statements", schema.getProperty(
            TestUtils.uri("advisor")).getEquivalentProperties().contains(
            TestUtils.uri("test:advisor")));
    }

    /**
     * scm-dom1
     */
    @Test
    public void testDomainSuperclass() throws Exception {
        Assert.assertTrue("Failed to infer domain: superclass from domain: " +
            "subclass", schema.getProperty(TestUtils.uri("hasAlumnus")).getDomain().contains(
            TestUtils.uri("Organization")));
    }

    /**
     * scm-dom2
     */
    @Test
    public void testDomainInheritance() throws Exception {
        Assert.assertTrue("Subproperty should inherit superproperty's domain",
            schema.getProperty(TestUtils.uri("headOf")).getDomain().contains(
            TestUtils.uri("Person")));
    }

    /**
     * scm-rng1
     */
    @Test
    public void testRangeSuperclass() throws Exception {
        Assert.assertTrue("Failed to infer range: superclass from range: " +
            "subclass", schema.getProperty(TestUtils.uri("advisor")).getRange().contains(
            TestUtils.uri("Faculty")));
    }

    /**
     * scm-rng2
     */
    @Test
    public void testRangeInheritance() throws Exception {
        Assert.assertTrue("Subproperty should inherit superproperty's range",
            schema.getProperty(TestUtils.uri("headOf")).getRange().contains(
            TestUtils.uri("Organization")));
    }


    /**
     * scm-hv
     */
    @Test
    public void testHasValueSubProp() throws Exception {
        Resource c1 = TestUtils.bnode("c1");
        Resource c2 = TestUtils.bnode("c2");
        schema.processTriple(TestUtils.statement(c1, OWL.HASVALUE,
            TestUtils.stringLiteral("big data")));
        schema.processTriple(TestUtils.statement(c1, OWL.ONPROPERTY,
            TestUtils.uri("researchSpecialty")));
        schema.processTriple(TestUtils.statement(c2, OWL.HASVALUE,
            TestUtils.stringLiteral("big data")));
        schema.processTriple(TestUtils.statement(c2, OWL.ONPROPERTY,
            TestUtils.uri("researchInterest")));
        schema.closure();
        Assert.assertTrue("[hasValue x on <subproperty>] should be subclass of"
            + " [hasValue x on <superproperty>]",
            schema.getClass(c1).getSuperClasses().contains(c2));
    }

    /**
     * scm-svf1
     */
    @Test
    public void testSomeValuesSubClass() throws Exception {
        Resource c1 = TestUtils.bnode("c1");
        Resource c2 = TestUtils.bnode("c2");
        schema.processTriple(TestUtils.statement(c1, OWL.SOMEVALUESFROM,
            TestUtils.uri("ResearchGroup")));
        schema.processTriple(TestUtils.statement(c1, OWL.ONPROPERTY,
            TestUtils.uri("worksFor")));
        schema.processTriple(TestUtils.statement(c2, OWL.SOMEVALUESFROM,
            TestUtils.uri("Organization")));
        schema.processTriple(TestUtils.statement(c2, OWL.ONPROPERTY,
            TestUtils.uri("worksFor")));
        schema.closure();
        Assert.assertTrue("[someValuesFrom <subclass> on p] should be subclass of"
            + " [someValuesFrom <superclass> on p]",
            schema.getClass(c1).getSuperClasses().contains(c2));
    }

    /**
     * scm-svf2
     */
    @Test
    public void testSomeValuesSubProp() throws Exception {
        Resource c1 = TestUtils.bnode("c1");
        Resource c2 = TestUtils.bnode("c2");
        schema.processTriple(TestUtils.statement(c1, OWL.SOMEVALUESFROM,
            TestUtils.uri("Organization")));
        schema.processTriple(TestUtils.statement(c1, OWL.ONPROPERTY,
            TestUtils.uri("worksFor")));
        schema.processTriple(TestUtils.statement(c2, OWL.SOMEVALUESFROM,
            TestUtils.uri("Organization")));
        schema.processTriple(TestUtils.statement(c2, OWL.ONPROPERTY,
            TestUtils.uri("memberOf")));
        schema.closure();
        Assert.assertTrue("[someValuesFrom y on <subproperty>] should be subclass of"
            + " [someValuesFrom y on <superproperty>]",
            schema.getClass(c1).getSuperClasses().contains(c2));
    }

    /**
     * scm-avf1
     */
    @Test
    public void testAllValuesSubClass() throws Exception {
        Resource c1 = TestUtils.bnode("c1");
        Resource c2 = TestUtils.bnode("c2");
        schema.processTriple(TestUtils.statement(c1, OWL.ALLVALUESFROM,
            TestUtils.uri("ResearchGroup")));
        schema.processTriple(TestUtils.statement(c1, OWL.ONPROPERTY,
            TestUtils.uri("worksFor")));
        schema.processTriple(TestUtils.statement(c2, OWL.ALLVALUESFROM,
            TestUtils.uri("Organization")));
        schema.processTriple(TestUtils.statement(c2, OWL.ONPROPERTY,
            TestUtils.uri("worksFor")));
        schema.closure();
        Assert.assertTrue("[allValuesFrom <subclass> on p] should be subclass of"
            + " [allValuesFrom <superclass> on p]",
            schema.getClass(c1).getSuperClasses().contains(c2));
    }

    /**
     * scm-avf2
     */
    @Test
    public void testAllValuesSubProp() throws Exception {
        Resource c1 = TestUtils.bnode("c1");
        Resource c2 = TestUtils.bnode("c2");
        schema.processTriple(TestUtils.statement(c1, OWL.ALLVALUESFROM,
            TestUtils.uri("University")));
        schema.processTriple(TestUtils.statement(c1, OWL.ONPROPERTY,
            TestUtils.uri("worksFor")));
        schema.processTriple(TestUtils.statement(c2, OWL.ALLVALUESFROM,
            TestUtils.uri("University")));
        schema.processTriple(TestUtils.statement(c2, OWL.ONPROPERTY,
            TestUtils.uri("memberOf")));
        schema.closure();
        Assert.assertTrue("[allValuesFrom y on <superproperty>] should be subclass of"
            + " [allValuesFrom y on <subproperty>]",
            schema.getClass(c2).getSuperClasses().contains(c1));
    }
}
