Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

518
LINES

< > BotCompany Repo | #1026002 // HCRUD_Concepts

JavaX fragment (include) [tags: use-pretranspiled]

Libraryless. Click here for Pure Java version (21685L/133K).

1  
sclass HCRUD_Concepts<A extends Concept> extends HCRUD_Data {
2  
  Concepts cc = db_mainConcepts();
3  
  Class<A> cClass;
4  
  new L<IVF1<A>> onCreateOrUpdate;
5  
  new L<IVF1<A>> onCreate;
6  
  IVF2<A, MapSO> afterUpdate; // parameter 2: old values
7  
  MapSO filters; // fields to filter by/add to new objects
8  
  SS ciFilters; // case-insensitive filters
9  
  IF1<Cl<A>> customFilter;
10  
  ValueConverterForField valueConverter;
11  
  bool referencesBlockDeletion;
12  
  bool trimAllSingleLineValues;
13  
  Set<S> fieldsToHideInCreationForm;
14  
  bool lockDB; // lock DB while updating object
15  
  bool verbose;
16  
  bool dropEmptyListValues = true;
17  
  bool lsMagic;
18  
  bool convertConceptValuesToRefs;
19  
  A currentConcept; // while editing
20  
  
21  
  bool useDynamicComboBoxes; // dynamically load concepts combo box entries for all fields
22  
  IPred<S> useDynamicComboBoxesForField; // activate dynamic combo boxes for selected fields
23  
  int dynamicComboBoxesThreshold = 1000; // if there are this many entries or more, use a dynamic combo box
24  
  
25  
  *(Class<A> *cClass) {}
26  
  *(Concepts *cc, Class<A> *cClass) {}
27  
  
28  
  // XXX - breaking change from just shortName()
29  
  swappable S itemName() { ret humanizeShortName(cClass); }
30  
  swappable S itemNamePlural() { ret super.itemNamePlural(); }
31  
  
32  
  //LS fields() { ret conceptFields(cClass); }
33  
  
34  
  L<A> itemsForListing() {
35  
    ret defaultSort(asList(listConcepts()));
36  
  }
37  
  
38  
  @Override
39  
  L<MapSO> list() {
40  
    //ret lazyMap itemToMapForList(itemsForListing());
41  
    ret lazyMap(itemsForListing(), a -> new Item(str(a.id)) {
42  
      public MapSO calcFullMap() { ret itemToMapForList(a); }
43  
    });
44  
  }
45  
  
46  
  // more efficient version - convert items only after taking subList
47  
  // TODO: this is never called by HCRUD; make lazy list instead?
48  
  @Override
49  
  L<MapSO> list(IntRange range) {
50  
    ret lambdaMap itemToMapForList(subListOrFull(itemsForListing(), range));
51  
  }
52  
  
53  
  Cl<A> listConcepts() {
54  
    Cl<A> l = listConcepts_firstStep();
55  
    ret postProcess(customFilter, l);
56  
  }
57  
    
58  
  swappable Cl<A> listConcepts_firstStep() {
59  
    if (empty(ciFilters))
60  
      ret conceptsWhere(cc, cClass, mapToParams(filters));
61  
    else if (empty(filters))
62  
      ret conceptsWhereCI(cc, cClass, mapToParams(ciFilters));
63  
    else {
64  
      // TODO: choose best index
65  
      Cl<A> l = conceptsWhere(cc, cClass, mapToParams(filters));
66  
      ret filterConceptsIC(l, mapToParams(ciFilters));
67  
    }
68  
  }
69  
  
70  
  swappable Pair<S, Bool> defaultSortField() { ret pair("id", true); }
71  
  
72  
  swappable L<A> defaultSort(L<A> l) { ret sortedByConceptIDDesc(l); }
73  
  
74  
  swappable A emptyConcept() {
75  
    ret unlisted(cClass);
76  
  }
77  
  
78  
  swappable MapSO emptyObject() {
79  
    A c = emptyConcept();
80  
    
81  
    // not actually necessary here, we do it on create
82  
    /*cset(c, mapToParams(filters));
83  
    cset(c, mapToParams(ciFilters));*/
84  
    
85  
    MapSO map = itemToMap(c);
86  
    //printVars_str emptyObject(+cClass, +filters, +ciFilters, +map);
87  
    
88  
    ret mapMinusKeys(fieldsToHideInCreationForm, map);
89  
  }
90  
  
91  
  MapSO itemToMap(A c) {
92  
    if (c == null) null;
93  
    ret putKeysFirst(getFieldOrder(c), conceptToMap_gen_withNullValues(c));
94  
  }
95  
  
96  
  MapSO itemToMapForList(A c) {
97  
    if (c == null) null;
98  
    MapSO map = itemToMap(c);
99  
    massageItemMapForList(c, map);
100  
    ret map;
101  
  }
102  
  
103  
  swappable void massageItemMapForList(A c, MapSO map) {
104  
  }
105  
  
106  
  swappable void massageItemMapForUpdate(A c, MapSO map) {
107  
    // Concepts.RefL magic
108  
    
109  
    Cl<Field> refLFields = nonStaticNonTransientFieldObjectsOfType(Concept.RefL.class, c);
110  
    printVars_str(+refLFields, +c);
111  
    for (Field f : refLFields) {
112  
      new TreeMap<Int, O> values;
113  
      new Matches m;
114  
      for (S key, O value : cloneMap(map))
115  
        if (startsWith(key, f.getName() + "_", m) && isInteger(m.rest())) {
116  
          Concept concept = getConceptFromString((S) value);
117  
          //printVars_str("RefL magic", +key, +conceptID, +concept);
118  
          if (concept != null || !dropEmptyListValues)
119  
            values.put(parseInt(m.rest()), concept);
120  
          map.remove(key);
121  
        }
122  
        
123  
      if (!dropEmptyListValues)
124  
        while (nempty(values) && lastValue(values) == null) {
125  
          if (verbose) print("Dropping value " + lastEntry(values));
126  
          removeLastKey(values);
127  
        }
128  
        
129  
      map.put(f.getName(), valuesAsList(values));
130  
    }
131  
    
132  
    // process dynamic bool, concept fields
133  
    
134  
    for (S name : cloneKeys(map)) {
135  
      S metaInfo = metaInfoFromForm(name);
136  
      print("metaInfo for " + name + ": " + metaInfo);
137  
      if (eqic(metaInfo, "concept"))
138  
        replaceStringValueWithConcept(map, name);
139  
      else if (eqic(metaInfo, "bool"))
140  
        replaceStringValueWithBool(map, name);
141  
    }
142  
    
143  
    // Concepts.Ref magic (look up concept)
144  
    
145  
    for (Field f : nonStaticNonTransientFieldObjectsOfType(Concept.Ref.class, c))
146  
      replaceStringValueWithConcept(map, f.getName());
147  
148  
    // trim values
149  
    
150  
    if (trimAllSingleLineValues)
151  
      for (Map.Entry<S, O> e : map.entrySet()) {
152  
        S val = optCastString(e.getValue());
153  
        if (val != null && isSingleLine(val) && isUntrimmed(val))
154  
          e.setValue(trim(val));
155  
      }
156  
157  
    // LS magic
158  
    
159  
    if (lsMagic)
160  
     for (Field f : nonStaticNonTransientFieldObjectsOfType(L.class, c)) {
161  
      if (eqOneOf(f.getName(), "refs", "backRefs")) continue;
162  
      new TreeMap<Int, S> values;
163  
      new Matches m;
164  
      
165  
      for (S key, O value : cloneMap(map)) {
166  
        if (startsWith(key, f.getName() + "_", m) && isInteger(m.rest())) {
167  
          if (!dropEmptyListValues || nempty((S) value)) {
168  
            if (verbose) print("Adding value " + m.rest() + " / " + value);
169  
            mapPut(values, parseInt(m.rest()), (S) value);
170  
          }
171  
          map.remove(key);
172  
        }
173  
      }
174  
      
175  
      if (!dropEmptyListValues)
176  
        while (nempty(values) && empty(lastValue(values))) {
177  
          if (verbose) print("Dropping value " + lastEntry(values));
178  
          removeLastKey(values);
179  
        }
180  
        
181  
      map.put(f.getName(), valuesAsList(values));
182  
    }
183  
    
184  
    // don't set SecretValue fields
185  
    
186  
    for (Field f : nonStaticNonTransientFieldObjectsOfType(SecretValue, c))
187  
      map.remove(f.getName());
188  
  }
189  
  
190  
  void replaceStringValueWithConcept(MapSO map, S key) {
191  
    O value = map.get(key);
192  
    if (value cast S) {
193  
      Concept concept = getConceptFromString(value);
194  
      map.put(key, concept);
195  
    }
196  
  }
197  
  
198  
  void replaceStringValueWithBool(MapSO map, S key) {
199  
    O value = map.get(key);
200  
    if (value cast S) {
201  
      map.put(key, englishStringToBool(value));
202  
    }
203  
  }
204  
  
205  
  swappable MapSO getObject(O id) {
206  
    ret itemToMap(conceptForID(id));
207  
  }
208  
  
209  
  MapSO getObjectForEdit(O id) {
210  
    currentConcept = conceptForID(id);
211  
    ret getObject(id);
212  
  }
213  
  
214  
  O createObject(SS fullMap, S fieldPrefix) {
215  
    rawFormValues = fullMap;
216  
    try {
217  
      SS map = extractFieldValues(fullMap, fieldPrefix);
218  
      A c = cnew(cc, cClass);
219  
      // make sure filters override
220  
      setValues(c, mapMinusKeys(map, filteredFields()), true);
221  
      cset(c, mapToParams(filters));
222  
      cset(c, mapToParams(ciFilters));
223  
      pcallFAll(onCreate, c);
224  
      pcallFAll(onCreateOrUpdate, c);
225  
      callOpt(c, "_onCreated"); // TODO: synchronize?
226  
      ret c.id;
227  
    } finally {
228  
      rawFormValues = null;
229  
    }
230  
  }
231  
  
232  
  void setValues(A c, SS map, bool creating) {
233  
    lock lockDB && !creating ? dbLock(cc) : null;
234  
    MapSO map2 = (Map) cloneMap(map);
235  
    massageItemMapForUpdate(c, map2);
236  
    if (verbose) {
237  
      print("setValues " + map);
238  
      print("backRefs: " + c.backRefs);
239  
    }
240  
    MapSO oldValues = !creating && afterUpdate != null
241  
      ? cgetAll_cloneLists(c, keys(map2)) : null;
242  
    if (convertConceptValuesToRefs)
243  
      convertAllConceptValuesToRefs(c, map2);
244  
    if (valueConverter == null)
245  
      cSmartSet(c, mapToParams(map2));
246  
    else
247  
      cSmartSet_withConverter_pcall(verbose, valueConverter, c, mapToParams(map2));
248  
    if (oldValues != null)
249  
      callF(afterUpdate, c, oldValues);
250  
    if (verbose)
251  
      print("backRefs: " + c.backRefs);
252  
  }
253  
  
254  
  // for dynamic fields
255  
  void convertAllConceptValuesToRefs(A c, MapSO map) {
256  
    for (S key, O value : cloneMap(map)) {
257  
      if (value cast Concept)
258  
        if (!hasField(c, key)) {
259  
          print("Converting value to ref: " + key);
260  
          map.put(key, c.new Ref(value));
261  
        }
262  
    }
263  
  }
264  
  
265  
  A conceptForID(O id) {
266  
    ret _getConcept(cc, cClass, toLong(id));
267  
  }
268  
  
269  
  S updateObject(O id, SS fullMap, S fieldPrefix) {
270  
    rawFormValues = fullMap;
271  
    try {
272  
      SS map = extractFieldValues(fullMap, fieldPrefix);
273  
      A c = conceptForID(id);
274  
      if (c == null) ret "Object " + id + " not found";
275  
      try answer checkFilters(c);
276  
      setValues(c, map, false);
277  
      pcallFAll(onCreateOrUpdate, c);
278  
      ret "Object " + id + " updated";
279  
    } finally {
280  
      rawFormValues = null;
281  
    }
282  
  }
283  
  
284  
  S deleteObject(O id) {
285  
    A c = conceptForID(id);
286  
    if (c == null) ret "Object " + id + " not found";
287  
    try answer checkFilters(c);
288  
    actuallyDeleteConcept(c);
289  
    ret "Object " + id + " deleted";
290  
  }
291  
  
292  
  swappable void actuallyDeleteConcept(A c) {
293  
    deleteConcept(c);
294  
  }
295  
  
296  
  S checkFilters(A c) {
297  
    if (!checkConceptFields(c, mapToParams(filters))
298  
     || !checkConceptFieldsIC(c, mapToParams(ciFilters)))
299  
      ret "Object " + c.id + " not in view";
300  
    ret "";
301  
  }
302  
  
303  
  HCRUD_Concepts<A> addFilters(MapSO map) {
304  
    fOr (S field, O value : map)
305  
      addFilter(field, value);
306  
    this;
307  
  }
308  
  
309  
  HCRUD_Concepts<A> addFilter(S field, O value) {
310  
    filters = orderedMapPutOrCreate(filters, field, value);
311  
    this;
312  
  }
313  
  
314  
  // TODO: do this the other way around (check for editable types)
315  
  swappable bool isEditableValue(O value) {
316  
    //if (value instanceof Concept.RefL) true; // subsumed in next line
317  
    if (value instanceof L) true;
318  
    if (value instanceof Cl) false;
319  
    true;
320  
  }
321  
  
322  
  Renderer getRenderer(S field) {
323  
    if (!isEditableValue(currentValue))
324  
      ret new NotEditable;
325  
      
326  
    Class type = fieldType(or(currentConcept, cClass), field);
327  
    S metaInfo = metaInfoFromForm(field);
328  
    //printVars_str("getRenderer", +cClass, +field, +type, +rawFormValues);
329  
    
330  
    if (eq(type, bool.class))
331  
      ret new CheckBox;
332  
      
333  
    if (eq(type, Bool.class))
334  
      ret new ComboBox(ll("", "yes", "no"),
335  
        b -> trueFalseNull((Bool) b, "yes", "no", ""));
336  
    
337  
    // show a Ref<> field as a combo box
338  
    
339  
    if (isSubtypeOf(type, Concept.Ref.class)) {
340  
      Class<? extends Concept> c = fieldTypeArg(field);
341  
      AbstractComboBox cb = makeConceptsComboBox(field, c);
342  
      cb.metaInfo = "concept";
343  
      ret cb;
344  
    }
345  
    
346  
    // show dynamic field with concept/ref value as combo box
347  
    
348  
    O val = deref(currentValue);
349  
    if (val cast Concept) {
350  
      Class<? extends Concept> c = val.getClass();
351  
      AbstractComboBox cb = makeConceptsComboBox(field, c);
352  
      cb.metaInfo = "concept";
353  
      ret cb;
354  
    }
355  
    
356  
    // show dynamic ref field from URL parameters
357  
    
358  
    if (eqic(metaInfo, "concept")) {
359  
      printVars_str("metaInfo value", +field, +val);
360  
      AbstractComboBox cb = makeConceptsComboBox(field, Concept); // TODO
361  
      cb.metaInfo = "concept";
362  
      ret cb;
363  
    }
364  
    
365  
    // show a RefL<> field as a list of combo boxes
366  
    
367  
    if (eq(type, Concept.RefL.class)) {
368  
      Class<? extends Concept> c = fieldTypeArg(field);
369  
      ret new FlexibleLengthList(makeConceptsComboBox(field, c));
370  
    }
371  
    
372  
    if (eq(type, L.class)) {
373  
      //Class c = fieldTypeArg(field);
374  
      ret new FlexibleLengthList(new TextField(80));
375  
    }
376  
    
377  
    if (val cast Bool)
378  
      ret new CheckBox;
379  
    
380  
    ret super.getRenderer(field);
381  
  }
382  
  
383  
  DynamicComboBox makeDynamicComboBox(S field, Class<? extends Concept> c) {
384  
    DynamicComboBox cb = new(field);
385  
    cb.valueToEntry = value -> {
386  
      value = deref(value);
387  
      //print("ComboBox: value type=" + _getClass(value);
388  
      long id = 0;
389  
      if (value cast Concept) id = conceptID(value);
390  
      else if (value instanceof S && isInteger((S) value)) id = parseLong(value);
391  
      if (id != 0)
392  
        ret comboBoxItem(_getConcept(cc, id));
393  
      null;
394  
    };
395  
    ret cb;
396  
  }
397  
398  
  AbstractComboBox makeConceptsComboBox(S field, Class<? extends Concept> c) {
399  
    if (c == null) fail("Null type for field \*field*/. currentConcept: " + currentConcept);
400  
401  
    if (useDynamicComboBoxes || useDynamicComboBoxesForField != null && useDynamicComboBoxesForField.get(field))
402  
      ret makeDynamicComboBox(field, c);
403  
    
404  
    Cl concepts = listConceptClass(c);
405  
    if (l(concepts) >= dynamicComboBoxesThreshold)
406  
      ret makeDynamicComboBox(field, c);
407  
    LS entries = comboBoxItems(concepts);
408  
409  
    ComboBox cb = new(entries);
410  
    cb.valueToEntry = value -> {
411  
      value = deref(value);
412  
      //print("ComboBox: value type=" + _getClass(value);
413  
      long id = 0;
414  
      if (value cast Concept) id = conceptID(value);
415  
      else if (value instanceof S && isInteger((S) value)) id = parseLong(value);
416  
      if (id != 0) {
417  
        S entry = firstWhereFirstLongIs(entries, id);
418  
        //print("combobox selected: " + id + " / " + entry);
419  
        ret entry;
420  
      }
421  
      null;
422  
    };
423  
    ret cb;
424  
  }
425  
  
426  
  // TODO: filters?
427  
  <A extends Concept> Cl<A> listConceptClass(Class<A> c) {
428  
    ret cc.list(c);
429  
  }
430  
  
431  
  LS comboBoxItemsForConceptClass(Class<? extends Concept> c) {
432  
    ret comboBoxItems(listConceptClass(c));
433  
  }
434  
  
435  
  LS comboBoxItems(Cl<? extends Concept> l) {
436  
    ret comboBoxItems_static(l);
437  
  }
438  
  
439  
  static LS comboBoxItems_static(Cl<? extends Concept> l) {
440  
    ret itemPlus("", lmap comboBoxItem_static(l));
441  
  }
442  
  
443  
  S comboBoxItem(Concept val) {
444  
    ret comboBoxItem_static(val);
445  
  }
446  
  
447  
  sS comboBoxItem_static(Concept val) {
448  
    ret val == null ? null : shorten(val.id + ": " + val);
449  
  }
450  
  
451  
  swappable bool objectCanBeDeleted(O id) {
452  
    ret !referencesBlockDeletion || !hasBackRefs(conceptForID(id));
453  
  }
454  
  
455  
  Set<S> filteredFields() { ret joinSets(keys(filters), keys(ciFilters)); }
456  
  
457  
  Class<? extends Concept> fieldTypeArg(S field) {
458  
    ret getTypeArgumentAsClass(genericFieldType(or(currentConcept, cClass), field));
459  
  }
460  
  
461  
  swappable Class<? extends Concept> conceptClassForComboBoxSearch(S info, S query) {
462  
    // info is the field name (Ref or RefL). get concept class
463  
    if (!isIdentifier(info)) ret cClass;
464  
    
465  
    S field = info;
466  
    Class<? extends Concept> c = fieldTypeArg(field);
467  
468  
    // simple hack to show list for dynamic/new fields
469  
    // assuming it's the same as the main class
470  
    // clients can improve on this
471  
    if (c == null) c = cClass;
472  
    
473  
    ret c;
474  
  }
475  
  
476  
  swappable LS comboBoxSearchBaseItems(S info, S query) {
477  
    var c = conceptClassForComboBoxSearch(info, query);
478  
    if (c == null) ret emptyList();
479  
    ret comboBoxItemsForConceptClass(c);
480  
  }
481  
  
482  
  LS comboBoxSearch(S info, S query) {
483  
    LS items = comboBoxSearchBaseItems(info, query);
484  
    ret takeFirst(10, scoredSearch(query, items));
485  
  }
486  
  
487  
  // to find the concept e.g. within massageFormMatrix
488  
  A conceptForMap(MapSO map) {
489  
    if (map == null) null;
490  
    long id = toLong(map.get(idField()));
491  
    ret id == 0 ? null : (A) _getConcept(cc, id);
492  
  }
493  
  
494  
  A getConcept(MapSO map) {
495  
    ret conceptForMap(map);
496  
  }
497  
  
498  
  SS extractFieldValues(SS fullMap, S fieldPrefix) {
499  
    ret subMapStartingWith_dropPrefix(fullMap, fieldPrefix);
500  
  }
501  
  
502  
  Concept getConceptFromString(S s) {
503  
    long conceptID = parseFirstLong(s);
504  
    ret _getConcept(cc, conceptID);
505  
  }
506  
  
507  
  S metaInfoFromForm(S field) {
508  
    ret mapGet(rawFormValues, "metaInfo_" + field);
509  
  }
510  
  
511  
  void addCIFilter(S field, S value) {
512  
    ciFilters = putOrCreate(ciFilters, field, value);
513  
  }
514  
  
515  
  swappable S titleForObjectID(O id) {
516  
    ret htmlEncode2(strOrNull(conceptForID(id)));
517  
  }
518  
}

download  show line numbers  debug dex  old transpilations   

Travelled to 7 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt, xrpafgyirdlv

No comments. add comment

Snippet ID: #1026002
Snippet name: HCRUD_Concepts
Eternal ID of this version: #1026002/201
Text MD5: 54c548eee75fe00ee9b2422e9ba511c3
Transpilation MD5: c29b4ffe951e292ffb159455b14be011
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2021-11-15 23:08:51
Source code size: 16155 bytes / 518 lines
Pitched / IR pitched: No / No
Views / Downloads: 918 / 2135
Version history: 200 change(s)
Referenced in: [show references]