cdc.applic.consistency.core.ConsistencyCheckerImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cdc-applic-consistency-core Show documentation
Show all versions of cdc-applic-consistency-core Show documentation
Applicabilities Consistency Core.
package cdc.applic.consistency.core;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import cdc.applic.consistency.Composition;
import cdc.applic.consistency.ConsistencyChecker;
import cdc.applic.consistency.ConsistencyData;
import cdc.applic.consistency.handlers.ConsistencyHandler;
import cdc.applic.consistency.issues.ConsistencyDataLocation;
import cdc.applic.consistency.issues.ConsistencyIssues;
import cdc.applic.consistency.issues.GlobalIssueType;
import cdc.applic.consistency.issues.NodeIssueType;
import cdc.applic.dictionaries.Dictionary;
import cdc.applic.dictionaries.Registry;
import cdc.applic.dictionaries.checks.WritingRuleChecker;
import cdc.applic.dictionaries.core.checks.DictionaryChecker;
import cdc.applic.dictionaries.handles.DictionaryHandle;
import cdc.applic.expressions.ApplicException;
import cdc.applic.expressions.Expression;
import cdc.applic.expressions.Expressions;
import cdc.applic.expressions.checks.ApplicIssue;
import cdc.applic.expressions.checks.CheckedData;
import cdc.applic.proofs.Prover;
import cdc.applic.proofs.ProverFeatures;
import cdc.applic.proofs.core.clauses.ProverImpl;
import cdc.applic.simplification.Simplifier;
import cdc.applic.simplification.SimplifierFeatures;
import cdc.applic.simplification.core.SimplifierImpl;
import cdc.issues.Diagnosis;
import cdc.issues.Issue;
public class ConsistencyCheckerImpl implements ConsistencyChecker {
@Override
public void check(ConsistencyData data,
ConsistencyHandler handler) {
final Executor computer = new Executor<>(data, handler);
computer.execute();
}
@Override
public List check(ConsistencyData data) {
final List events = new ArrayList<>();
final ConsistencyHandler handler = new ConsistencyHandler() {
@Override
public void beginAnalysis() {
// Ignore
}
@Override
public void endAnalysis() {
// Ignore
}
@Override
public void issue(Issue issue) {
events.add(issue);
}
};
check(data, handler);
return events;
}
private static class Executor {
private final ConsistencyData data;
private final ConsistencyHandler handler;
/** Set of processed nodes. **/
private final Set processed = new HashSet<>();
/** Set of collected policies. */
private final Set dictionaries = new HashSet<>();
private final Map actualApplic = new HashMap<>();
private final Map handles = new HashMap<>();
private final Map provers = new HashMap<>();
private final boolean usableData = true;
// TODO merged dictionary for actual applic
public Executor(ConsistencyData data,
ConsistencyHandler handler) {
this.data = data;
this.handler = handler;
}
private DictionaryHandle getDictionaryHandle(Dictionary dictionary) {
return dictionary == null
? null
: handles.computeIfAbsent(dictionary, DictionaryHandle::new);
}
private DictionaryHandle getLocalHandle(N node) {
return getDictionaryHandle(data.getNodeDictionary(node));
}
private Expression getActualApplic(N node) {
return actualApplic.get(node);
}
private Dictionary getActualDictionary(N node) {
// TODO MergedDictionary
return data.getNodeDictionary(node);
}
private Prover getProver(Dictionary dictionary) {
return provers.computeIfAbsent(dictionary,
p -> new ProverImpl(getDictionaryHandle(p),
ProverFeatures.INCLUDE_ASSERTIONS_ALL_POSSIBLE_RESERVES));
}
private B asBlock(N node) {
@SuppressWarnings("unchecked")
final B block = (B) node;
return block;
}
private R asReference(N node) {
@SuppressWarnings("unchecked")
final R reference = (R) node;
return reference;
}
private String toString(N node,
boolean capital,
boolean applic) {
final StringBuilder builder = new StringBuilder();
builder.append(ConsistencyData.getPath(data, node, capital));
if (applic) {
builder.append(" (")
.append(data.getNodeLocalApplicability(node))
.append(')');
}
return builder.toString();
}
private static String toString(Expression expression) {
return expression.getContent();
}
/**
* Collect data before running checks.
*/
private void prepareData() {
for (final B root : data.getRootBlocks()) {
prepareData(root);
}
processed.clear();
}
/**
* Collects data related to a Node:
*
* - Policies
*
- Actual applicabilities
*
*
* @param node The node.
*/
private void prepareData(N node) {
processed.add(node);
final Dictionary dictionary = data.getNodeDictionary(node);
if (dictionary == null) {
issue(NodeIssueType.NULL_DICTIONARY,
toString(node, true, false) + " dictionary cannot be found.",
node);
} else {
dictionaries.add(dictionary);
if (data.isBlock(node)) {
computeActualApplic(asBlock(node));
for (final N child : data.getBlockChildren(asBlock(node))) {
prepareData(child);
if (data.isReference(child)) {
computeActualApplic(asBlock(node), asReference(child));
}
}
}
}
}
private Expression simplify(Expression expression,
Dictionary dictionary) {
final Simplifier simplifier = new SimplifierImpl(getDictionaryHandle(dictionary));
final SimplifierFeatures features =
SimplifierFeatures.builder()
.proverFeatures(ProverFeatures.INCLUDE_ASSERTIONS_ALL_POSSIBLE_RESERVES)
.allHints()
.build();
try {
return simplifier.simplify(expression, features).getValue();
} catch (final ApplicException e) {
return expression;
}
}
private void computeActualApplic(B block) {
if (!actualApplic.containsKey(block)) {
final List effectives = new ArrayList<>();
for (final B parent : data.getBlockParents(block)) {
computeActualApplic(parent);
effectives.add(getActualApplic(parent));
}
effectives.add(data.getNodeLocalApplicability(block));
actualApplic.put(block,
simplify(Expressions.SHORT_NARROW_NO_SIMPLIFY.and(effectives),
data.getNodeDictionary(block)));
}
}
private void computeActualApplic(B parent,
R reference) {
if (!actualApplic.containsKey(reference)) {
final Expression parentActual = actualApplic.get(parent);
final Expression local = data.getNodeLocalApplicability(reference);
final Expression effective = Expressions.SHORT_NARROW_NO_SIMPLIFY.and(parentActual, local);
actualApplic.put(reference,
simplify(effective,
data.getNodeDictionary(reference)));
}
}
public void execute() {
handler.beginAnalysis();
prepareData();
checkPolicies();
if (usableData) {
checkNodes();
}
handler.endAnalysis();
}
private void checkPolicies() {
final Set registries = new HashSet<>();
for (final Dictionary dictionary : dictionaries) {
final DictionaryChecker checker = new DictionaryChecker(dictionary);
final List issues = new ArrayList<>();
checker.check(issues);
handler.issues(issues);
// for (final ApplicIssue issue : issues) {
// fireDictionaryIssue(dictionary, issue.getDescription());
// }
registries.add(dictionary.getRegistry());
}
if (registries.size() > 1) {
issue(GlobalIssueType.REGISTRY_CONSISTENCY,
"Policies are not based on the same registry.");
}
}
private void checkNodes() {
for (final B root : data.getRootBlocks()) {
checkNode(root);
}
}
private void checkNode(N node) {
processed.add(node);
final boolean localCompliance = checkLocalApplicCompliance(node);
if (localCompliance) {
// Not necessary to check actual compliance if local compliance is absent
checkActualApplicCompliance(node);
}
if (data.isBlock(node)) {
checkBlockComposition(asBlock(node));
for (final N child : data.getBlockChildren(asBlock(node))) {
checkNode(child);
}
} else {
checkReference(asReference(node));
}
}
/**
* Checks the compliance of a node applicability restriction with its dictionary.
*
* @param node The node.
* @return {@code true} if compliance is OK.
*/
private boolean checkLocalApplicCompliance(N node) {
final Expression applic = data.getNodeLocalApplicability(node);
final DictionaryHandle handle = getLocalHandle(node);
if (handle != null) {
final Diagnosis diagnosis = WritingRuleChecker.check(handle,
new CheckedData(data.getSystemId() + "/"
+ toString(node, true, true), applic));
handler.issues(diagnosis.getIssues());
return diagnosis.isOk();
} else {
return false;
}
}
private void checkActualApplicCompliance(N node) {
final Expression applic = getActualApplic(node);
final DictionaryHandle handle = getLocalHandle(node);
if (handle != null) {
final Diagnosis diagnosis = WritingRuleChecker.check(getLocalHandle(node),
new CheckedData(data.getSystemId() + "/"
+ toString(node, true, false), applic));
handler.issues(diagnosis.getIssues());
}
}
private void checkNonEmptyIntersection(B parent,
N child) {
final Expression parentApplic = getActualApplic(parent);
final Expression childApplic = getActualApplic(child);
final Prover prover = getProver(getActualDictionary(child));
final boolean success = prover.intersects(parentApplic, childApplic);
if (!success) {
issue(NodeIssueType.ACTUAL_CHILD_PARENT_COMPLIANCE,
toString(child, true, true) + " is never applicable in its parent."
+ toString(parent, false, true) + ".",
child);
}
}
private void checkBlockComposition(B block) {
final Composition composition = data.getBlockComposition(block);
if (composition != Composition.ANY) {
final List childrenBlocks = new ArrayList<>();
final List childrenApplic = new ArrayList<>();
for (final N child : data.getBlockChildren(block)) {
if (data.isBlock(child)) {
childrenBlocks.add(asBlock(child));
childrenApplic.add(getActualApplic(child));
}
}
if (childrenBlocks.isEmpty()) {
if (composition == Composition.AT_LEAST_ONE) {
issue(NodeIssueType.AT_LEAST_ONE_COMPLIANCE,
toString(block, true, true) + " has no applicable children.",
block);
} else if (composition == Composition.EXACTLY_ONE) {
issue(NodeIssueType.EXACTLY_ONE_COMPLIANCE,
toString(block, true, true) + " has no applicable children.",
block);
}
} else {
final Expression context = getActualApplic(block);
final Expression[] expressions = childrenApplic.toArray(new Expression[childrenApplic.size()]);
final Prover prover = getProver(getActualDictionary(block));
if (composition == Composition.AT_LEAST_ONE) {
final boolean success = prover.alwaysAtLeastOneInContext(context, expressions);
if (!success) {
issue(NodeIssueType.AT_LEAST_ONE_COMPLIANCE,
toString(block, true, true) + " does not have at least one applicable child.",
block);
}
} else if (composition == Composition.EXACTLY_ONE) {
final boolean success = prover.alwaysExactlyOneInContext(context, expressions);
if (!success) {
issue(NodeIssueType.EXACTLY_ONE_COMPLIANCE,
toString(block, true, true) + " does not have exactly one applicable child.",
block);
}
} else {
final boolean success = prover.alwaysAtMostOneInContext(context, expressions);
if (!success) {
issue(NodeIssueType.AT_MOST_ONE_COMPLIANCE,
toString(block, true, true) + " does not have at most one applicable child.",
block);
}
}
}
}
for (final N child : data.getBlockChildren(block)) {
checkNonEmptyIntersection(block, child);
}
}
private void checkReference(R ref) {
final B target = data.getReferenceTarget(ref);
if (target == null) {
issue(NodeIssueType.NULL_TARGET,
toString(ref, true, false) + " target cannot be found.",
ref);
} else {
final Prover prover = getProver(getActualDictionary(ref));
final Expression inner = getActualApplic(ref);
final Expression outer = getActualApplic(target);
final boolean included = prover.contains(outer, inner);
if (!included) {
issue(NodeIssueType.ACTUAL_REF_TARGET_COMPLIANCE,
"Actual applic of " + toString(ref, false, false) + " (" + toString(inner)
+ ") is not included in actual applic of its target "
+ toString(target, false, false) + " (" + toString(outer) + ").",
ref);
}
}
}
private void issue(GlobalIssueType type,
String description) {
handler.issue(ConsistencyIssues.builder()
.name(type)
.description(description)
.addLocation(new ConsistencyDataLocation(data))
.build());
}
private void issue(NodeIssueType type,
String description,
N node) {
handler.issue(ConsistencyIssues.builder()
.name(type)
.description(description)
.addLocation(new ConsistencyDataLocation(data, node))
.build());
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy