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) { err.dest._removeBackRef(err.ref); out.add(" backreference dropped"); } else out.add(" TODO - fix not implemented"); } ret lines(out); } S runAndFixAll() { run(); ret fixAll(); } }