// assumes there is a S name field in the concept
// not serializable
sclass NameBasedVoiceCRUD {
transient Concepts cc = db_mainConcepts();
Class conceptClass;
S concept, concepts; // singular & plural of what we are managing
// e.g. +scenario. must be non-null
// string fields in the scope are treated case-insensitive
O[] scope = new O[0];
L onSelect;
transient A selected;
*() {}
*(Class conceptClass, S conceptName, O... scope) {
this(db_mainConcepts(), conceptClass, conceptName, scope);
}
*(Concepts *cc, Class *conceptClass, S conceptName, O... scope) {
concept = conceptName;
concepts = plural(conceptName);
this.scope = unnull(scope);
}
S answer(S s) null {
new Matches m;
if (match("how many \*concepts*/", s))
ret str(countConceptsCI(cc, conceptClass, unnull(scope)));
if (match("delete \*concept*/ *", s, m)) {
S name = $1;
select(null);
A sc = findConceptWithName(name);
if (sc == null)
ret format("\*firstToUpper(concept)*/ * not found", name);
unregisterConcept(sc);
addUndo(new UndoDeleteConcept(sc));
ret format("Scenario * deleted", getName(sc));
}
if (match("new \*concept*/ *", s, m)) {
S name = $1;
A sc = conceptWhereCI(cc, conceptClass, paramsPlus(scope, +name));
if (sc != null)
ret format("\*firstToUpper(concept)*/ * exists", name);
A a = uniqCI(cc, conceptClass, paramsPlus(scope, +name));
select(a);
addUndo(new UndoCreateConcept(a.id));
ret format("\*firstToUpper(concept)*/ * created", name);
}
if (match("list \*concepts*/", s))
ret or2(joinWithComma(collect name(conceptsWhereCI(cc, conceptClass, unnull(scope)))), "No \*concepts*/ defined");
if (match("select \*concept*/ *", s, m)) {
A sc = findConceptWithName($1);
if (sc == null)
ret format("\*firstToUpper(concept)*/ * not found", $1);
select(sc);
ret format("\*firstToUpper(concept)*/ * selected", getName(sc));
}
if (match("unselect \*concept*/", s)) {
if (selected == null) ret "OK";
select(null);
ret "OK, scenario unselected";
}
if (match("rename \*concept*/ * to *", s, m)) {
S name1 = $1, name2 = $2;
if (!eqic(name1, name2)
&& conceptWhereCI(cc, conceptClass, paramsPlus(scope, name := name2)) != null)
ret format("A \*concept*/ named * exists", name2);
A sc = findConceptWithName(name1);
if (sc == null)
ret format("\*firstToUpper(concept)*/ * not found", name1);
addUndo(new UndoSetConceptField(sc.id, 'name, name1, name2));
cset(sc, name := name2);
select(sc);
ret format("\*firstToUpper(concept)*/ * renamed to *", name1, name2);
}
}
// this is more of a search function, not an exact name lookup
swappable A findConceptWithName(S name) {
Cl l = conceptsWhereCI(cc, conceptClass, scope);
A _a = objectWhereIC(l, +name);
if (_a != null) ret _a;
ScoredSearcher searcher = new(name);
for (A a : l) searcher.put(a, getName(a));
ret searcher.best();
}
swappable S getName(A a) { ret getString name(a); }
swappable void addUndo(UndoableWithAnswer undo) {}
swappable void select(A a) { selected = a; pcallFAll(onSelect, a); }
void onSelect(IVF1 f) {
onSelect = listCreateAndAdd(onSelect, f);
}
}