Clause_9_4_5_Checker.java 9.46 KB
Newer Older
/*
 * Copyright 2020 ETSI
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package fr.emse.gitlab.saref.checkers;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;

import fr.emse.gitlab.saref.managers.ThemisManager;
import org.semanticweb.owl.explanation.api.Explanation;
import org.semanticweb.owl.explanation.api.ExplanationGenerator;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.profiles.OWL2DLProfile;
import org.semanticweb.owlapi.profiles.OWLProfileReport;
import org.semanticweb.owlapi.profiles.violations.UseOfDefinedDatatypeInDatatypeRestriction;
import org.semanticweb.owlapi.profiles.violations.UseOfDefinedDatatypeInLiteral;
import org.semanticweb.owlapi.profiles.violations.UseOfUndeclaredDatatype;
import org.semanticweb.owlapi.profiles.violations.UseOfUnknownDatatype;

import fr.emse.gitlab.saref.SAREFPipelineException;
import fr.emse.gitlab.saref.managers.OntologyManager;
import fr.emse.gitlab.saref.managers.RepositoryManager;
Maxime Lefrançois's avatar
Maxime Lefrançois committed
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.semanticweb.owlapi.reasoner.TimeOutException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
 * Checks TS 103 673 Clause 9.4.5: OWL Profile, Consistency, and Satisfiability
 * of Classes
 */
public class Clause_9_4_5_Checker extends AbstractClauseChecker {

	private static enum MESSAGE {
		profile, consistent, satisfiable, oops, oopsError;
	}

	/**
	 */
	public Clause_9_4_5_Checker(RepositoryManager repositoryManager) {
		super(repositoryManager, Clause_9_4_5_Checker.class);
	}

	@Override
	public void checkClause() throws SAREFPipelineException {
		final OWLOntology ontology = pipeline.getOntologyManager().loadOntology(version, errorLogger);
		if (ontology == null) {
			return;
		}

		final OWLProfileReport report = new OWL2DLProfile().checkOntology(ontology);
		report.getViolations().removeIf(v->{
			if(v instanceof UseOfDefinedDatatypeInDatatypeRestriction
					|| v instanceof UseOfDefinedDatatypeInLiteral
					|| v instanceof UseOfUndeclaredDatatype
					|| v instanceof UseOfUnknownDatatype) {
				return true;
			}
			return false;
		});
Maxime Lefrançois's avatar
Maxime Lefrançois committed
		if (!report.getViolations().isEmpty()) {
			String violations = report.getViolations().stream().map(Object::toString)
					.collect(Collectors.joining("\n- ", "\n\n- ", "\n\n"));
			logError(getMessage(MESSAGE.profile, violations));
		}
		OntologyManager ontologyManager = pipeline.getOntologyManager();

		Set<Explanation<OWLAxiom>> incExplanation = ontologyManager.getInconsistenceExplanations(ontology, errorLogger);
		if (incExplanation != null && !incExplanation.isEmpty()) {
			String explanation = incExplanation.stream().map(e -> e.getAxioms().toString())
					.collect(Collectors.joining("\n- ", "\n\n- ", "\n\n"));
			logError(getMessage(MESSAGE.consistent, explanation));
		} else {
			ExplanationGenerator<OWLAxiom> generator = ontologyManager.getExplanationGenerator(ontology);
			ontology.classesInSignature().forEach(c -> {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
				if (c.isOWLNothing()) {
					return;
				}
				Set<Explanation<OWLAxiom>> incExplanation2 = ontologyManager.getIncoherenceExplanations(generator, c);
				if (!incExplanation2.isEmpty()) {
					String explanation = incExplanation2.stream().map(e -> e.getAxioms().toString())
							.collect(Collectors.joining("\n- ", "\n\n- ", "\n\n"));
					logError(getMessage(MESSAGE.satisfiable, c, explanation));
		try {
			checkOops();
		} catch (Exception ex) {
			logWarning(getMessage(MESSAGE.oopsError), ex);
	public void checkOops() throws Exception {

		ByteArrayOutputStream out = new ByteArrayOutputStream();
		String onto = "";

		Model model = version.getModel();
Maxime Lefrançois's avatar
Maxime Lefrançois committed
		model.write(out, "RDF/XML"); // write in string and then replace
		onto = out.toString().replaceAll("]]", "] ]");
Maxime Lefrançois's avatar
Maxime Lefrançois committed
		String xmlRequest = "<?xml version='1.0' encoding='UTF-8'?>" + "<OOPSRequest>" + "<OntologyURI></OntologyURI>"
Salva5297's avatar
Salva5297 committed
				+ "<OntologyContent><![CDATA[" + onto + "]]></OntologyContent>" + "<Pitfalls></Pitfalls>"
Maxime Lefrançois's avatar
Maxime Lefrançois committed
				+ "<OutputFormat>RDF/XML</OutputFormat>" + "</OOPSRequest>";
Maxime Lefrançois's avatar
Maxime Lefrançois committed
		try {

			OkHttpClient httpClient = new OkHttpClient().newBuilder().build();

			MediaType mediaType = MediaType.parse("application/xml");

			RequestBody body = RequestBody.create(xmlRequest, mediaType);

Maxime Lefrançois's avatar
Maxime Lefrançois committed
			Request request = new Request.Builder().url("http://oops.linkeddata.es/rest").method("POST", body)
					.addHeader("Content-Type", "application/xml").build();
			Response response;

			try{
				response = httpClient.newCall(request).execute();
			catch (SocketTimeoutException e){
				httpClient = new OkHttpClient().newBuilder().readTimeout(10, TimeUnit.SECONDS).build();
				try{
					response = httpClient.newCall(request).execute();
				}
				catch (SocketTimeoutException f){
					httpClient = new OkHttpClient().newBuilder().readTimeout(20, TimeUnit.SECONDS).build();
					response = httpClient.newCall(request).execute();
				}
			if (response.code() != 200) {
				throw new SAREFPipelineException("response", "Unexpected response code " + response.code());
			}
			String result = response.body().string();
			Document doc = ThemisManager.convertStringToXMLDocument(result);

			NodeList nodeList = doc.getElementsByTagName("rdf:Description");

			ArrayList<String> oopsErrors = new ArrayList<String>();
			for (int temp = 0; temp < nodeList.getLength(); temp++){
				org.w3c.dom.Node node = nodeList.item(temp);
				if (node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
					Element element = (Element) node;
					if (((Element) node).getElementsByTagName("oops:hasCode").item(0) != null) {
						if (((Element) node).getElementsByTagName("oops:hasName").item(0) != null) {
							String code = element.getElementsByTagName("oops:hasCode").item(0).getTextContent();
							String name = element.getElementsByTagName("oops:hasName").item(0).getTextContent();
							String description = element.getElementsByTagName("oops:hasDescription").item(0).getTextContent();
							String affected = "";
							NodeList affectedElements = element.getElementsByTagName("oops:hasAffectedElement");
							if (affectedElements != null) {
								for (int k = 0; k < affectedElements.getLength(); k++) {
									if (k == 0) {
										affected += "\"" + affectedElements.item(k).getTextContent() + "\"";
									} else {
										affected += " ; \"" + affectedElements.item(k).getTextContent() + "\"";
									}
								}
							}


							String oopsError = String.format("Code: %s, Name: %s, Description: %s, Affected Elements: %s", code, name, description, affected);
							oopsErrors.add(oopsError);
						}
						else{
							String code = element.getElementsByTagName("oops:hasCode").item(0).getTextContent();
							String description = element.getElementsByTagName("oops:hasDescription").item(0).getTextContent();
							String affected = "";
							NodeList affectedElements = element.getElementsByTagName("oops:hasAffectedElement");
							if (affectedElements != null) {
								for (int k = 0; k < affectedElements.getLength(); k++) {
									if (k == 0) {
										affected += "\"" + affectedElements.item(k).getTextContent() + "\"";
									} else {
										affected += " ; \"" + affectedElements.item(k).getTextContent() + "\"";
									}
								}
							}


							String oopsError = String.format("Code: %s, Description: %s, Affected Elements: %s", code, description, affected);
							oopsErrors.add(oopsError);
						}
					}
				}
			}
			if (!oopsErrors.isEmpty()) {
				String data = oopsErrors.stream().map(e -> e.toString())
						.collect(Collectors.joining("\n- ", "\n\n- ", "\n\n"));
				logWarning(getMessage(MESSAGE.oops, data));
		} catch (IOException | SAREFPipelineException e) {
			logWarning(getMessage(MESSAGE.oopsError), e);