blob: 1e4d2c9e6360307e1c0b9abae61a2e7861ef198c [file] [log] [blame]
/*
* 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.shex.sys;
import java.util.*;
import org.apache.jena.atlas.lib.InternalErrorException;
import org.apache.jena.atlas.lib.Pair;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.Triple;
import org.apache.jena.shex.*;
import org.apache.jena.shex.expressions.ShapeExpression;
import org.apache.jena.shex.expressions.TripleExpression;
import org.apache.jena.shex.semact.SemanticActionPlugin;
/**
* Context for a validation and collector of the results.
*/
public class ValidationContext {
private final ShexSchema shapes;
private final Graph data;
private boolean verbose = false;
private boolean seenReportEntry = false;
private ValidationContext parentCtx = null;
private Map<String, SemanticActionPlugin> semActPluginIndex;
// <data node, shape>
private Deque<Pair<Node, ShexShape>> inProgress = new ArrayDeque<>();
private final ShexReport.Builder reportBuilder = ShexReport.create();
/** @deprecated Use method {@link #create()} */
@Deprecated(forRemoval = true)
public static ValidationContext create(ValidationContext vCxt) {
return vCxt.create();
}
public ValidationContext(Graph data, ShexSchema shapes) {
this(data, shapes, null);
}
/**
* Precondition: vCxt cannot be null
*
* @param vCxt
*/
private ValidationContext(ValidationContext vCxt) {
this(vCxt, vCxt.data, vCxt.shapes, vCxt.inProgress, vCxt.semActPluginIndex);
}
public ValidationContext(Graph data, ShexSchema shapes, Map<String, SemanticActionPlugin> semActPluginIndex) {
this(null, data, shapes, null, semActPluginIndex);
}
private ValidationContext(ValidationContext parentCtx, Graph data, ShexSchema shapes, Deque<Pair<Node, ShexShape>> progress, Map<String, SemanticActionPlugin> semActPluginIndex) {
this.parentCtx = parentCtx;
this.data = data;
this.shapes = shapes;
this.semActPluginIndex = semActPluginIndex;
if (progress != null)
this.inProgress.addAll(progress);
}
public ValidationContext getParent() {
return parentCtx;
}
public ValidationContext getRoot() {
ValidationContext parent = this.parentCtx;
while (parent != null) {
parent = this.getParent();
}
return (parent != null) ? parent : this;
}
public TripleExpression getTripleExpression(Node label) {
return shapes.getTripleExpression(label);
}
public ShexSchema getShapes() {
return shapes;
}
public ShexShape getShape(Node label) {
return shapes.get(label);
}
public Graph getData() {
return data;
}
/**
* Creates a new validation context with the current one as its parent context.
* Initializes the new context with the state of the parent context.
*
* @return new ValidationContext with this as parent.
*/
public ValidationContext create() {
// Fresh ShexReport.Builder
return new ValidationContext(this, this.data, this.shapes, this.inProgress, this.semActPluginIndex);
}
public void startValidate(ShexShape shape, Node data) {
inProgress.push(Pair.create(data, shape));
}
// Return true if done or in-progress (i.e. don't walk further)
public boolean cycle(ShexShape shape, Node data) {
return inProgress.stream().anyMatch(p -> p.equalElts(data, shape));
}
public boolean dispatchStartSemanticAction(ShexSchema schema, ValidationContext vCxt) {
return !schema.getSemActs().stream().anyMatch(semAct -> {
String semActIri = semAct.getIri();
SemanticActionPlugin semActPlugin = this.semActPluginIndex.get(semActIri);
if (semActPlugin != null) {
if (!semActPlugin.evaluateStart(semAct, schema)) {
vCxt.reportEntry(new ReportItem(String.format("%s start shape failed", semActIri), null));
return true;
}
}
return false;
});
}
public boolean dispatchShapeExprSemanticAction(ShapeExpression se, Node focus) {
return !se.getSemActs().stream().anyMatch(semAct -> {
SemanticActionPlugin semActPlugin = this.semActPluginIndex.get(semAct.getIri());
if (semActPlugin != null) {
if (!semActPlugin.evaluateShapeExpr(semAct, se, focus))
return true;
}
return false;
});
}
public boolean dispatchTripleExprSemanticAction(TripleExpression te, Set<Triple> matchables) {
return !te.getSemActs().stream().anyMatch(semAct -> {
SemanticActionPlugin semActPlugin = this.semActPluginIndex.get(semAct.getIri());
if (semActPlugin != null) {
if (!semActPlugin.evaluateTripleExpr(semAct, te, matchables))
return true;
}
return false;
});
}
public void finishValidate(ShexShape shape, Node data) {
Pair<Node, ShexShape> x = inProgress.pop();
if (x.equalElts(data, shape))
return;
throw new InternalErrorException("Eval stack error");
}
// In ShEx "satisfies" returns a boolean.
// public boolean conforms() { return validationReportBuilder.isEmpty(); }
public boolean hasEntries() {
return reportBuilder.hasEntries();
}
/**
* Update other with "this" state
*/
public void copyInto(ValidationContext other) {
reportBuilder.getItems().forEach(item -> other.reportEntry(item));
reportBuilder.getReports().forEach(reportLine -> other.shexReport(reportLine));
}
private void shexReport(ShexRecord reportLine) {
reportBuilder.shexReport(reportLine);
}
/**
* Current state.
*/
public List<ReportItem> getReportItems() {
return reportBuilder.getItems();
}
/**
* Current state.
*/
public List<ShexRecord> getShexReportItems() {
return reportBuilder.getReports();
}
public void reportEntry(ReportItem item) {
reportBuilder.addReportItem(item);
}
public void shexReport(ShexRecord entry, Node focusNode, ShexStatus result, String reason) {
reportBuilder.shexReport(entry, focusNode, result, reason);
}
public ShexReport generateReport() {
return reportBuilder.build();
}
}