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() 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; } // overridable 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); cset(c, mapToParams(filters)); setValues(c, map); 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"; 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"; deleteConcept(c); ret "Object deleted"; } 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 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 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 c) { ret itemPlus("", map(cc.list(c), val -> shorten(val.id + ": " + val))); } }