sclass HCRUD_Concepts extends HCRUD_Data {
Concepts cc = db_mainConcepts();
Class cClass;
new L> onCreateOrUpdate;
MapSO filters; // fields to filter by/add to new objects
ValueConverterForField valueConverter;
*(Class *cClass) {}
// XXX - breaking change from just shortName()
swappable S itemName() { ret humanizeShortName(cClass); }
//LS fields() { ret conceptFields(cClass); }
L list() {
ret lambdaMap itemToMapForList(defaultSort(asList(listConcepts())));
}
Cl listConcepts() {
ret conceptsWhere(cc, cClass, mapToParams(filters));
}
L defaultSort(L l) { ret l; }
MapSO emptyObject() {
A c = unlisted(cClass);
print("fieldOrder", getFieldOrder(c));
ret printStruct("emptyObject", itemToMap(c));
}
MapSO itemToMap(A c) {
if (c == null) null;
ret putKeysFirst(getFieldOrder(c), conceptToMap_gen_withNullValues(c));
}
MapSO itemToMapForList(A c) {
if (c == null) null;
MapSO map = itemToMap(c);
massageItemMapForList(c, map);
ret map;
}
swappable void massageItemMapForList(A c, MapSO map) {
}
// overridable
void massageItemMapForUpdate(A c, MapSO map) {
// Concepts.RefL magic
for (Field f : nonStaticNonTransientFieldObjectsOfType(Concept.RefL.class, cClass)) {
new TreeMap values;
new Matches m;
for (S key, O value : cloneMap(map))
if (startsWith(key, f.getName() + "_", m) && isInteger(m.rest())) {
long conceptID = parseFirstLong((S) value);
Concept concept = getConcept(cc, conceptID);
printVars_str("RefL magic", +key, +conceptID, +concept);
mapPut(values, parseInt(m.rest()), concept);
map.remove(key);
}
map.put(f.getName(), valuesAsList(values));
}
// Concepts.Ref magic (look up concept)
for (Field f : nonStaticNonTransientFieldObjectsOfType(Concept.Ref.class, cClass)) {
O value = map.get(f.getName());
if (value cast S) {
long conceptID = parseFirstLong(value);
Concept concept = getConcept(cc, conceptID);
map.put(f.getName(), concept);
}
}
}
MapSO getObject(O id) {
ret itemToMap(getConcept(cc, cClass, toLong(id)));
}
O createObject(SS map) {
A c = cnew(cc, cClass);
setValues(c, map);
// make sure filters override
cset(c, mapToParams(filters));
pcallFAll(onCreateOrUpdate, c);
ret c.id;
}
void setValues(A c, SS map) {
MapSO map2 = (Map) cloneMap(map);
massageItemMapForUpdate(c, map2);
if (valueConverter == null)
cSmartSet(c, mapToParams(map2));
else
cSmartSet_withConverter(valueConverter, c, mapToParams(map2));
}
S updateObject(O id, SS map) {
A c = getConcept(cc, cClass, toLong(id));
if (c == null) ret "Object " + id + " not found";
try answer checkFilters(c);
setValues(c, map);
pcallFAll(onCreateOrUpdate, c);
ret "Object " + id + " updated";
}
S deleteObject(O id) {
A c = getConcept(cc, cClass, toLong(id));
if (c == null) ret "Object not found";
try answer checkFilters(c);
deleteConcept(c);
ret "Object deleted";
}
S checkFilters(A c) {
ret checkConceptFields(c, mapToParams(filters)) ? "" : "Object " + c.id + " not in view";
}
HCRUD_Concepts addFilters(MapSO map) {
fOr (S field, O value : map)
addFilter(field, value);
this;
}
HCRUD_Concepts addFilter(S field, O value) {
filters = orderedMapPutOrCreate(filters, field, value);
this;
}
Renderer getRenderer(S field) {
Class type = fieldType(cClass, field);
if (eq(type, bool.class))
ret new CheckBox();
// show a Ref<> field as a combo box
if (eq(type, Concept.Ref.class)) {
Class extends Concept> c = getTypeArgumentAsClass(genericFieldType(cClass, field));
ret makeComboBox(comboBoxItemsForConceptClass(c));
}
// show a RefL<> field as a list of combo boxes
if (eq(type, Concept.RefL.class)) {
Class extends Concept> c = getTypeArgumentAsClass(genericFieldType(cClass, field));
LS items = comboBoxItemsForConceptClass(c);
ret new FlexibleLengthList(makeComboBox(items));
}
ret super.getRenderer(field);
}
ComboBox makeComboBox(LS entries) {
ComboBox cb = new(entries);
cb.valueToEntry = value -> {
if (value cast Concept) ret firstWhereFirstLongIs(entries, conceptID(value));
null;
};
ret cb;
}
LS comboBoxItemsForConceptClass(Class extends Concept> c) {
ret itemPlus("", map(cc.list(c), val -> shorten(val.id + ": " + val)));
}
}