cdc.applic.dictionaries.impl.SectionAssertionsImpl Maven / Gradle / Ivy
Show all versions of cdc-applic-dictionaries-impl Show documentation
package cdc.applic.dictionaries.impl;
import java.io.PrintStream;
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.dictionaries.Constraint;
import cdc.applic.dictionaries.DictionaryMembership;
import cdc.applic.dictionaries.items.Assertion;
import cdc.applic.dictionaries.items.ConstraintAssertion;
import cdc.applic.dictionaries.items.ContextAssertion;
import cdc.applic.dictionaries.items.DItem;
import cdc.applic.dictionaries.items.LocalAssertion;
import cdc.applic.dictionaries.items.StandardAssertion;
import cdc.applic.dictionaries.visitors.AddMissingPrefixes;
import cdc.applic.expressions.Expression;
import cdc.applic.expressions.Expressions;
import cdc.graphs.core.GraphPath;
import cdc.tuples.Tuple2;
import cdc.util.debug.Printables;
import cdc.util.debug.Verbosity;
import cdc.util.function.IterableUtils;
import cdc.util.lang.Checks;
final class SectionAssertionsImpl implements SectionAssertions {
private static final String ASSERTION = "assertion";
private static final String CONSTRAINT = "constraint";
private static final String CONTEXT = "context";
private static final String EXPRESSION = "expression";
private static final Expressions EXPR = Expressions.SHORT_NARROW_SIMPLIFY;
private final AbstractDictionaryImpl dictionary;
/** All local assertions: user-defined, constraint and context. */
private final Set local = new HashSet<>();
/** The local context assertion. */
private ContextAssertionImpl localContext;
/** The local constraint assertions indexed by their (constraint, params). */
private final Map, ConstraintAssertion> localConstraint = new HashMap<>();
/** All assertions: local and derived. */
private final Set all = new HashSet<>();
/** Derived Standard assertions, indexed by their source. */
private final Map derivedStandard = new HashMap<>();
/** Derived context assertions. */
private final Set derivedContext = new HashSet<>();
SectionAssertionsImpl(AbstractDictionaryImpl dictionary,
Expression context) {
this.dictionary = dictionary;
this.localContext = new ContextAssertionImpl(dictionary, context);
// Avoid adding tautology
if (!context.isTrue()) {
this.all.add(this.localContext);
}
}
/**
* Collects and derives all assertions from parent dictionaries.
*
* This MUST be called when a dictionary is created.
*/
void build() {
// There is no descendant at the time of call
// This does not matter
addDerivationsRec();
}
void refreshDerivationsRec() {
removeDerivationsRec();
addDerivationsRec();
}
/**
* Removes all derived assertions from this dictionary and all its descendants.
*/
private void removeDerivationsRec() {
for (final AbstractDictionaryImpl descendant : dictionary.structure.getSortedDescendants(true)) {
descendant.assertions.all.removeAll(descendant.assertions.derivedStandard.values());
descendant.assertions.all.removeAll(descendant.assertions.derivedContext);
descendant.assertions.derivedStandard.clear();
descendant.assertions.derivedContext.clear();
descendant.newCachesSerial(false);
}
}
/**
* Adds all derived (standard and context) assertions to this dictionary and all its descendants.
*/
private void addDerivationsRec() {
// Iterate on all descendants, including itself
for (final AbstractDictionaryImpl descendant : dictionary.structure.getSortedDescendants(true)) {
// Iterate on all ancestors of descendant.
for (final AbstractDictionaryImpl ancestor : descendant.structure.getSortedAncestors(false)) {
// Derive all standard assertions of ancestor
for (final LocalAssertion assertion : ancestor.assertions.local) {
if (assertion instanceof StandardAssertion) {
descendant.assertions.addDerivedStandard((StandardAssertion) assertion, ancestor);
}
}
}
descendant.assertions.addDerivedContext();
descendant.newCachesSerial(false);
}
}
/**
* Computes the derived context assertion and adds it to this dictionary.
*/
private void addDerivedContext() {
// Compute all paths from roots to this dictionary
final List> paths = new ArrayList<>();
dictionary.structure.explorePathsFromRoots(paths::add);
// disjunction of conjunctions from paths
// C1C2.. + C1C3... +
addDerivedContext(buildOr(paths));
// final int size = paths.size();
// final List> parentPaths = new ArrayList<>();
// for (final GraphPath path : paths) {
// parentPaths.add(path.parent());
// }
//
// // for each pair of paths, declare an exclusion
// // !C1C2... and !C1C3... and ...
// // This can be done if paths share a common element that is not the last one
// for (int i1 = 0; i1 < size - 1; i1++) {
// final GraphPath parent1 = parentPaths.get(i1);
// for (int i2 = i1 + 1; i2 < size; i2++) {
// final GraphPath parent2 = parentPaths.get(i2);
// if (parent1.hasCommonItemsWith(parent2)) {
// addDerivedContext(buildNotAnd(paths.get(i1), paths.get(i2)));
// }
// }
// }
}
/**
* Creates a derived context assertion from an expression, and adds it to this dictionary.
*
* @param x The expression.
*/
private void addDerivedContext(Expression x) {
// Avoid adding tautology
if (!x.isTrue()) {
final DerivedContextAssertionImpl assertion = new DerivedContextAssertionImpl(dictionary, x);
derivedContext.add(assertion);
all.add(assertion);
}
}
/**
* Returns the conjunction of contexts associated to a path of dictionaries.
*
* All contexts except the last one are used.
*
* @param path The path of dictionaries.
* @return The conjunction of contexts of dictionaries contained in {@code path}.
*/
private static Expression buildAnd(GraphPath path) {
final List items = path.getItems();
Expression x = Expression.TRUE;
for (final AbstractDictionaryImpl d : items.subList(0, items.size() - 1)) {
x = EXPR.and(x, AddMissingPrefixes.execute(d.getContextExpression(), d));
}
return x;
}
/**
* Returns the disjunction of context expressions associated to each path.
*
* @param paths The dictionary paths.
* @return The disjunction of context expressions associated to each element of {@code path}.
*/
private static Expression buildOr(List> paths) {
if (paths.isEmpty()) {
return Expression.TRUE;
} else {
Expression x = Expression.FALSE;
for (final GraphPath path : paths) {
x = Expressions.SHORT_NARROW_SIMPLIFY.or(x, buildAnd(path));
}
return x;
}
}
// private static Expression buildNotAnd(GraphPath path1,
// GraphPath path2) {
// final Expression x = EXPR.not(EXPR.and(buildAnd(path1), buildAnd(path2)));
// return x;
// }
/**
* Returns the precondition part corresponding to a path between an ancestor
* of a dictionary and the dictionary.
*
* All paths between an ancestor and a dictionary are explored, each producing a part
* which are combined to produce the precondition.
*
* @param path The path.
* @return An expression corresponding to {@code path}.
*/
private static Expression buildStandardPrecondition(GraphPath path) {
Expression x = Expression.TRUE;
final List items = path.getItems();
// Ignore last path item. It corresponds to the target dictionary
for (final AbstractDictionaryImpl d : items.subList(0, items.size() - 1)) {
x = EXPR.and(x, d.getContextExpression());
}
return x;
}
/**
* Returns the precondition to be used with standard assertions defined in an ancestor dictionary.
*
* Derivation of standard assertion A define in {@code ancestor} produces
* an assertion of the form: {@code precondition -> A}.
*
* The precondition is computed from context expressions of all paths between {@code ancestor}
* and this dictionary.
*
* @param ancestor The ancestor dictionary.
* @return The standard precondition to be used with {@code ancestor}.
*/
private Expression getStandardPrecondition(AbstractDictionaryImpl ancestor) {
final List xs = new ArrayList<>();
dictionary.structure.explorePathsFromAncestor(ancestor, p -> xs.add(buildStandardPrecondition(p)));
Expression x = xs.isEmpty()
? Expression.TRUE
: EXPR.or(xs);
x = EXPR.and(getContextExpression(), x);
return x;
}
/**
* Adds a standard assertion to this dictionary and
* recursively adds its derivation to descendants.
*
* @param assertion The local assertion.
*/
private void addStandardRec(StandardAssertion assertion) {
Checks.isNotNull(assertion, ASSERTION);
// Local processing
addStandard(assertion);
// Derivation
for (final AbstractDictionaryImpl descendant : dictionary.structure.getSortedDescendants(false)) {
descendant.assertions.addDerivedStandard(assertion, dictionary);
}
}
/**
* Adds a standard assertion to this dictionary.
*
* @param assertion The assertion.
*/
private void addStandard(StandardAssertion assertion) {
local.add(assertion);
all.add(assertion);
if (assertion instanceof ConstraintAssertion) {
final ConstraintAssertion ga = (ConstraintAssertion) assertion;
localConstraint.put(Tuple2.of(ga.getConstraint(), ga.getParams()), ga);
}
dictionary.newCachesSerial(false);
}
/**
* Computes the derived assertion corresponding to a source standard assertion,
* and adds it to this dictionary.
*
* @param sourceAssertion The source standard assertion.
* @param sourceDictionary The source dictionary.
*/
private void addDerivedStandard(StandardAssertion sourceAssertion,
AbstractDictionaryImpl sourceDictionary) {
Checks.isTrue(dictionary != sourceDictionary, "Can not created a standard derivation in same dictionary");
if (!derivedStandard.containsKey(sourceAssertion)) {
final Expression derivedExpression = deriveStandard(sourceAssertion, sourceDictionary);
final DerivedStandardAssertionImpl derivedAssertion =
new DerivedStandardAssertionImpl(dictionary,
sourceAssertion,
sourceDictionary,
derivedExpression);
derivedStandard.put(sourceAssertion, derivedAssertion);
all.add(derivedAssertion);
dictionary.newCachesSerial(false);
}
}
/**
* Derives a standard assertion.
*
* @param sourceAssertion The source assertion to derive.
* @param sourceDictionary The dictionary containing the source assertion.
* @return The derivation of {@code sourceAssertion}.
*/
private Expression deriveStandard(StandardAssertion sourceAssertion,
AbstractDictionaryImpl sourceDictionary) {
final Expression precondition = getStandardPrecondition(sourceDictionary);
final Expression prefixedSourceExpression = AddMissingPrefixes.execute(sourceAssertion.getExpression(), sourceDictionary);
if (precondition.isTrue()) {
return prefixedSourceExpression;
} else {
return EXPR.imp(precondition, prefixedSourceExpression);
}
}
/**
* Removes a standard assertion and recursively removes its derivations.
*
* @param assertion The assertion.
*/
private void removeStandardRec(StandardAssertion assertion) {
Checks.isNotNull(assertion, ASSERTION);
// Local processing
removeStandard(assertion);
// Derivation
for (final AbstractDictionaryImpl descendant : dictionary.structure.getSortedDescendants(false)) {
descendant.assertions.removeStandardDerivation(assertion);
}
}
/**
* Locally remove a standard assertion.
*
* @param assertion The assertion.
*/
private void removeStandard(StandardAssertion assertion) {
final boolean removed = local.remove(assertion);
Checks.assertTrue(removed, "Unknown assertion");
all.remove(assertion);
if (assertion instanceof ConstraintAssertion) {
final ConstraintAssertion ga = (ConstraintAssertion) assertion;
final ConstraintAssertion r = localConstraint.remove(Tuple2.of(ga.getConstraint(), ga.getParams()));
Checks.assertTrue(r != null, "Unknown generated assertion");
}
dictionary.newCachesSerial(false);
}
/**
* Locally removes the derivation of a standard assertion.
*
* @param sourceAssertion The source assertion.
*/
private void removeStandardDerivation(StandardAssertion sourceAssertion) {
final DerivedStandardAssertionImpl removed = derivedStandard.remove(sourceAssertion);
Checks.assertTrue(removed != null, "No associated derived assertion");
all.remove(removed);
dictionary.newCachesSerial(false);
}
@Override
public void setContextExpression(Expression context) {
Checks.isNotNull(context, CONTEXT);
Checks.isFalse(dictionary.getParents().isEmpty(), "Can not set context on root dictionary.");
this.all.remove(this.localContext);
this.localContext = new ContextAssertionImpl(dictionary, context);
// Avoid adding tautology
if (!context.isTrue()) {
this.all.add(this.localContext);
}
removeDerivationsRec();
addDerivationsRec();
}
@Override
public Expression getContextExpression() {
return localContext.getExpression();
}
@Override
public Iterable extends T> getAssertions(Class cls) {
return IterableUtils.filterAndConvert(cls, all);
}
@Override
public Set getAllAssertions() {
return all;
}
@Override
public DictionaryMembership getMembership(Assertion assertion) {
Checks.isNotNull(assertion, ASSERTION);
if (local.contains(assertion)) {
return DictionaryMembership.LOCAL;
} else if (all.contains(assertion)) {
if (assertion instanceof ContextAssertion) {
return DictionaryMembership.CONTEXT;
} else {
return DictionaryMembership.DERIVED;
}
} else {
return DictionaryMembership.UNRELATED;
}
}
@Override
public void removeAssertion(StandardAssertion assertion) {
Checks.isNotNull(assertion, ASSERTION);
removeStandardRec(assertion);
}
@Override
public UserDefinedAssertionImpl createAssertion(Expression expression) {
Checks.isNotNull(expression, EXPRESSION);
final UserDefinedAssertionImpl assertion = new UserDefinedAssertionImpl(dictionary, expression);
addStandardRec(assertion);
return assertion;
}
@Override
public UserDefinedAssertionImpl createAssertion(String expression) {
Checks.isNotNull(expression, EXPRESSION);
return createAssertion(new Expression(expression));
}
@Override
public void removeRelatedAndDerivedAssertions(Constraint constraint) {
Checks.isNotNull(constraint, CONSTRAINT);
final List toRemove = new ArrayList<>();
for (final LocalAssertion assertion : local) {
if (assertion instanceof ConstraintAssertion) {
final ConstraintAssertion g = (ConstraintAssertion) assertion;
if (g.getConstraint() == constraint) {
toRemove.add(g);
}
}
}
for (final ConstraintAssertion assertion : toRemove) {
removeAssertion(assertion);
}
}
@Override
public Iterable getRelatedAssertions(Constraint constraint) {
Checks.isTrue(constraint.getOwner() == dictionary, "Dictionary mismatch");
final List result = new ArrayList<>();
for (final LocalAssertion assertion : getAssertions(LocalAssertion.class)) {
if (assertion instanceof ConstraintAssertion) {
result.add((ConstraintAssertion) assertion);
}
}
return result;
}
@Override
public ConstraintAssertion getRelatedAssertion(Constraint constraint,
String params) {
Checks.isTrue(constraint.getOwner() == dictionary, "Dictionary mismatch");
return this.localConstraint.get(Tuple2.of(constraint, params));
}
@Override
public ConstraintAssertion createAssertion(Constraint constraint,
String params,
Expression expression) {
Checks.isNotNull(constraint, CONSTRAINT);
Checks.isNotNull(expression, EXPRESSION);
Checks.isTrue(constraint.getOwner() == dictionary, "Non local constraint");
final ConstraintAssertionImpl assertion = new ConstraintAssertionImpl(constraint, params, expression);
addStandardRec(assertion);
return assertion;
}
@Override
public void printAssertions(PrintStream out,
int level,
Verbosity verbosity) {
// Assertions
Printables.indent(out, level);
out.println("Assertions (" + getAllAssertions().size() + ")");
if (verbosity != Verbosity.ESSENTIAL) {
for (final Assertion assertion : IterableUtils.toSortedList(getAllAssertions(), DItem.COMPARATOR)) {
Printables.indent(out, level + 1);
out.println(getMembership(assertion) + " " + assertion + " " + assertion.getKind());
}
}
}
}