Warning: session_start(): open(/var/lib/php/sessions/sess_tdumtv5e3a5aev60sc1n4k0bgg, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
sclass ConceptsRefChecker {
Concepts cc;
new L errors;
bool veryVerbose;
sclass Err {}
srecord ErrNoBackRef(Concept.Ref ref, Concept dest) > Err {}
srecord ErrSuperfluousBackRef(Concept.Ref ref, Concept dest) > Err {}
srecord ErrDuplicateRef(Concept c) > Err {}
srecord ErrNullRef(Concept c) > Err {}
srecord ErrMissingRef(Concept c, Concept.Ref ref) > Err {}
srecord ErrSuperfluousRef(Concept c, Concept.Ref ref) > Err {}
// a reference from a concept that is not in the concepts list
srecord ErrDanglingSource(Concept.Ref ref, Concept dest) > Err {}
// a reference to a concept that is not in the concepts list
srecord ErrDanglingDestination(Concept.Ref ref, Concept dest) > Err {}
*() {}
*(Concepts *cc) {}
L run() {
errors.clear();
if (cc == null) ret errors;
Cl concepts = cc.allConcepts();
if (veryVerbose)
print("Checking " + nConcepts(concepts));
for (Concept c : concepts) try {
O badElement = firstElementNotSubclassing(c.backRefs, Concept.Ref.class);
if (badElement != null) continue with print("Bad element in backRefs of " + c + ": " + badElement);
for (Concept.Ref ref : cloneList(c.backRefs)) {
if (ref! != c)
errors.add(new ErrSuperfluousBackRef(ref, c));
if (ref.concept()._concepts != cc)
errors.add(new ErrDanglingSource(ref, c));
}
Cl refs = c._refs();
badElement = firstElementNotSubclassing(refs, Concept.Ref.class);
if (badElement != null) continue with print("Bad element in backRefs of " + c + ": " + badElement);
if (!allUnique(refs))
errors.add(new ErrDuplicateRef(c));
if (contains(refs, null))
errors.add(new ErrNullRef(c));
refs = nonNulls(refs);
Cl actualRefs = scanConceptForRefs(c);
for (Concept.Ref ref : listMinusSet(refs, actualRefs))
errors.add(new ErrSuperfluousRef(c, ref));
for (Concept.Ref ref : listMinusSet(actualRefs, refs))
errors.add(new ErrMissingRef(c, ref));
for (Concept.Ref ref : refs)
if (ref.has()) {
if (!contains(ref->backRefs, ref))
errors.add(new ErrNoBackRef(ref, c));
if (ref->_concepts != cc)
errors.add(new ErrDanglingDestination(ref, c));
}
} catch print e {
print("Error processing concept " + c);
}
ret errors;
}
L errors() { ret errors; }
S fixAll() {
new LS out;
// start from end because list removes should be faster like this
for (Err err : reversed(cloneAndClear(errors))) pcall {
out.add("Processing " + err);
if (err cast ErrNoBackRef) {
err.ref.index();
out.add(" reference added");
} else if (err cast ErrSuperfluousRef) {
err.ref.unindexAndDrop();
out.add(" reference dropped");
} else if (err cast ErrDanglingSource) {
err.ref->_removeBackRef(err.ref);
out.add(" reference removed from destination");
} else if (err cast ErrDanglingDestination) {
err.ref->_removeBackRef(err.ref);
out.add(" reference removed from destination");
} else if (err cast ErrMissingRef) {
err.ref.registerRef();
out.add(" reference registered");
} else if (err cast ErrSuperfluousBackRef)
c._removeBackRef(ref);
out.add(" backreference dropped");
} else
out.add(" TODO - fix not implemented");
}
ret lines(out);
}
S runAndFixAll() { run(); ret fixAll(); }
}