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

1260
LINES

< > BotCompany Repo | #1004863 // "Concepts" (persistent Java objects, include)

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

Libraryless. Click here for Pure Java version (23573L/144K).

1  
// A database system for only slightly modified Java objects.
2  
3  
// Recent changes:
4  
// -minimal crash recovery disabled for now (could reenable)
5  
// -idCounter.structure file no longer used
6  
// -allDynamic/safeLoad disabled, it doesn't really make any sense
7  
// -conceptsFile can be in any directory now
8  
9  
// Functions that should always be there for child processes:
10  
please include function dbLock.
11  
12  
static int concepts_internStringsLongerThan = 10;
13  
14  
static new ThreadLocal<Bool> concepts_unlisted;
15  
16  
// BREAKING CHANGE 2021/6/7 set to true
17  
sbool concepts_unlistedByDefault = true; // true = we can create instances of concepts with "new" without registering them automatically
18  
19  
interface IConceptIndex {
20  
  void update(Concept c); // also for adding
21  
  void remove(Concept c);
22  
}
23  
24  
interface IFieldIndex<A extends Concept, Val> {
25  
  Cl<A> getAll(Val val);
26  
  L<Val> allValues(); // returns a cloned list
27  
  MultiSet<Val> allValues_multiSet();
28  
  ItIt<A> objectIterator();
29  
}
30  
31  
// Approach to persisting the Concepts object itself (in normal
32  
// DB operation, this is not done): For simplification, speed and
33  
// compactness, we make almost all the fields transient and store only // the concepts and the idCounter. To unstructure the Concepts object,
34  
// use unstructureConcepts() or postUnstructureConcepts(), then
35  
// re-set up any indices, listeners etc.
36  
37  
// base class indicating nothing
38  
sclass ConceptsChange {}
39  
40  
// change of a single concept
41  
srecord ConceptCreate(Concept c) extends ConceptsChange {}
42  
43  
// change of a single concept
44  
srecord ConceptChange(Concept c) extends ConceptsChange {}
45  
46  
// removal of a single concept
47  
// c.id is going to become 0 at some point, so we pass the
48  
// id separately
49  
srecord ConceptDelete(long id, Concept c) extends ConceptsChange {}
50  
51  
// unknown change anywhere in concepts; consider it all dirty
52  
// (this one should not be used except for batch jobs)
53  
srecord FullChange extends ConceptsChange {}
54  
55  
sclass Concepts implements AutoCloseable {
56  
  NavigableMap<Long, Concept> concepts = synchroTreeMap();
57  
  
58  
  // ID of last created concept
59  
  gettable long idCounter;
60  
  
61  
  transient HashMap<Class, O> perClassData;
62  
  transient Map miscMap; // don't use directly, call miscMap... methods to access
63  
  
64  
  // set to "-" for non-persistent (possibly not implemented)
65  
  // also, can include a case ID ("#123/1")
66  
  // TODO: phase out (we have conceptsFile field now)
67  
  transient S programID;
68  
  transient File conceptsFile;
69  
  
70  
  transient Concepts parent; // new mechanism
71  
  transient volatile long changes, changesWritten, lastChange;
72  
  transient volatile java.util.Timer autoSaver;
73  
  transient volatile bool dontSave;
74  
  transient volatile bool savingConcepts, noXFullGrab;
75  
  transient bool vmBusSend = true;
76  
  transient bool initialSave = false; // set to false to avoid initial useless saving
77  
  transient int autoSaveInterval = -1000; // 1 second + wait logic
78  
  transient bool useGZIP = true, quietSave;
79  
  transient ReentrantLock lock = new ReentrantLock(true);
80  
  transient ReentrantLock saverLock = new ReentrantLock(true);
81  
  transient long lastSaveTook = -1, lastSaveWas, loadTook, uncompressedSize;
82  
  transient float maxAutoSavePercentage = 10;
83  
  transient L<IConceptIndex> conceptIndices;
84  
  transient Map<Class<? extends Concept>, Map<S, IFieldIndex>> fieldIndices;
85  
  transient Map<Class<? extends Concept>, Map<S, IFieldIndex>> ciFieldIndices;
86  
  //transient L saveActions = synchroList();
87  
  transient L<Runnable> preSave;
88  
  transient O classFinder = _defaultClassFinder();
89  
  transient L onAllChanged = synchroList(); // list of runnables
90  
  transient new Set<IVF1> onChange;
91  
  transient O saveWrapper; // VF1<Runnable>, to profile saving
92  
  settable transient bool modifyOnCreate; // set _modified == created initially
93  
  transient bool modifyOnBackRef; // set modified if back refs change
94  
  transient bool useFileLock = true; // instead of locking by bot
95  
  settable transient bool grabThroughSocket = true; // probably never want to do this again actually
96  
  // OLD - not done anymore. transient bool collectForwardRefs
97  
  transient FileBasedLock fileLock;
98  
  transient bool storeBaseClassesInStructure; // helps with schema evolution when concept subclasses disappear
99  
  transient bool useBackRefsForSearches; // assume backRefs are sane in order to speed up searches
100  
  transient bool defunct;
101  
  transient int newBackupEveryXMinutes = 60;
102  
  settable transient bool closeConceptsOnExit;
103  
  settable transient Throwable saveError;
104  
  event fireSaveError(Throwable error);
105  
  
106  
  // add more fields for Concepts here
107  
  
108  
  *() {}
109  
  *(S *programID) {}
110  
  *(File *conceptsFile) {}
111  
  
112  
  synchronized long internalID() {
113  
    do {
114  
      ++idCounter;
115  
    } while (hasConcept(idCounter));
116  
    ret idCounter;
117  
  }
118  
  
119  
  synchronized HashMap<Class, O> perClassData() {
120  
    if (perClassData == null) perClassData = new HashMap;
121  
    ret perClassData;
122  
  }
123  
  
124  
  void initProgramID() {
125  
    if (programID == null)
126  
      programID = getDBProgramID();
127  
  }
128  
  
129  
  // load from structure
130  
  Concepts load(S structure, bool allDynamic default false) {
131  
    clearConcepts();
132  
    Map<Long, Concept> map = unstructureMap(structure, allDynamic, classFinder);
133  
    concepts.putAll(map);
134  
    assignConceptsToUs();
135  
    calcIdCounter();
136  
    this;
137  
  }
138  
  
139  
  Concepts load() {
140  
    initProgramID();
141  
    
142  
    // try custom grabber
143  
    O dbGrabber = miscMapGet("dbGrabber");
144  
    if (dbGrabber != null && !isFalse(callF(dbGrabber)))
145  
      this;
146  
147  
    try {
148  
      if (grabThroughSocket && tryToGrab()) ret this;
149  
    } catch e {
150  
      if (!exceptionMessageContains(e, "no xfullgrab"))
151  
        printShortException(e);
152  
      print("xfullgrab failed - loading DB of " + programID + " from disk");
153  
    }
154  
    ret loadFromDisk();
155  
  }
156  
  
157  
  Concepts loadFromDisk() {
158  
    if (nempty(concepts)) clearConcepts();
159  
    
160  
    // minimal crash recovery (disabled for now)
161  
    //restoreLatestBackupIfConceptsFileEmpty(programID, doIt := true);
162  
163  
    long time = now();
164  
    Map<Long, Concept> _concepts = cast unstructureGZFile(conceptsFile(), toIF1(classFinder));
165  
    putAll(concepts, _concepts);
166  
    assignConceptsToUs();
167  
    loadTook = now()-time;
168  
    done("Loaded " + n2(l(concepts), "concept"), time);
169  
    calcIdCounter();
170  
    this;
171  
  }
172  
  
173  
  Concepts loadConcepts() { ret load(); }
174  
  
175  
  bool tryToGrab() {
176  
    if (sameSnippetID(programID, getDBProgramID())) false;
177  
    temp RemoteDB db = connectToDBOpt(programID);
178  
    if (db != null) {
179  
      loadGrab(db.fullgrab());
180  
      true;
181  
    }
182  
    false;
183  
  }
184  
  
185  
  Concepts loadGrab(S grab) {
186  
    clearConcepts();
187  
    DynamicObject_loading.set(true);
188  
    try {
189  
      Map<Long, Concept> map = (Map) unstructure(grab, false, classFinder);
190  
      concepts.putAll(map);
191  
      assignConceptsToUs();
192  
      for (long l : map.keySet())
193  
        idCounter = max(idCounter, l);
194  
    } finally {
195  
      DynamicObject_loading.set(null);
196  
    }
197  
    //XXX allChanged(); // Nobody is listening at this point anyway
198  
    ret this;
199  
  }
200  
  
201  
  void assignConceptsToUs() {
202  
    // fix unstructure bugs
203  
    
204  
    for (Pair<Long, O> p: mapToPairs((Map<Long, O>) (Map) concepts))
205  
      if (!(p.b instanceof Concept)) {
206  
       print("DROPPING non-existant concept " + p.a + ": " + dynShortName(p.b));
207  
       concepts.remove(p.a);
208  
    }
209  
    
210  
    for (Concept c : values(concepts)) c._concepts = this;
211  
    for (Concept c : values(concepts))
212  
      c._doneLoading2(); // doneLoading2 is called on all concepts after all concepts are loaded
213  
  }
214  
215  
  S progID() {
216  
    ret programID == null ? getDBProgramID() : programID;
217  
  }
218  
  
219  
  Concept getConcept(S id) {
220  
    ret empty(id) ? null : getConcept(parseLong(id));
221  
  }
222  
  
223  
  Concept getConcept(long id) {
224  
    ret (Concept) concepts.get((long) id);
225  
  }
226  
  
227  
  Concept getConcept(RC ref) {
228  
    ret ref == null ? null : getConcept(ref.longID());
229  
  }
230  
  
231  
  bool hasConcept(long id) {
232  
    ret concepts.containsKey((long) id);
233  
  }
234  
  
235  
  void deleteConcept(long id) {
236  
    Concept c = getConcept(id);
237  
    if (c == null)
238  
      print("Concept " + id + " not found");
239  
    else
240  
      c.delete();
241  
  }
242  
  
243  
  void calcIdCounter() {
244  
    Long lastID = lastKey(concepts);
245  
    idCounter = lastID == null ? 1 : lastID+1;
246  
  }
247  
  
248  
  File conceptsDir() { ret dirOfFile(conceptsFile()); }
249  
  
250  
  selfType conceptsFile(File conceptsFile) { this.conceptsFile = conceptsFile; this; }
251  
  
252  
  File conceptsFile() {
253  
    if (conceptsFile != null) ret conceptsFile;
254  
    
255  
    ret getProgramFile(programID, useGZIP ? "concepts.structure.gz" : "concepts.structure");
256  
  }
257  
  
258  
  // used for locking when useFileLock is activated
259  
  File lockFile() {
260  
    ret newFile(conceptsDir(), "concepts.lock");
261  
  }
262  
  
263  
  FileBasedLock fileLock() {
264  
    if (fileLock == null)
265  
      fileLock = new FileBasedLock(lockFile());
266  
    ret fileLock;
267  
  }
268  
  
269  
  void saveConceptsIfDirty() { saveConcepts(); }
270  
  void save() { saveConcepts(); }
271  
272  
  void saveConcepts() {
273  
    vmBus_send saveConceptsCalled(Concepts.this);
274  
    if (dontSave) ret;
275  
    initProgramID();
276  
    saverLock.lock();
277  
    savingConcepts = true;
278  
    long start = now(), time;
279  
    try {
280  
      S s = null;
281  
      //synchronized(main.class) {
282  
      long _changes = changes;
283  
      if (_changes == changesWritten) ret;
284  
      
285  
      File f = conceptsFile();
286  
      
287  
      lock.lock();
288  
      long fullTime = now();
289  
      try {
290  
        if (useGZIP) {
291  
          vmBus_send callingSaveWrapper(Concepts.this, saveWrapper);
292  
          callRunnableWithWrapper(saveWrapper, r {
293  
            vmBus_send callingPreSave(Concepts.this, preSave);
294  
            callFAll(preSave);
295  
            vmBus_send writingFile(Concepts.this, f);
296  
            uncompressedSize = saveGZStructureToFile(f, cloneMap(concepts), makeStructureData());
297  
            vmBus_send gzFileSaved(Concepts.this, f, uncompressedSize);
298  
          });
299  
          newFile(conceptsDir(), "concepts.structure").delete();
300  
        } else
301  
          s = fullStructure();
302  
      } finally {
303  
        lock.unlock();
304  
      }
305  
      
306  
      /*while (nempty(saveActions))
307  
        pcallF(popFirst(saveActions));*/
308  
309  
      changesWritten = _changes; // only update when structure didn't fail
310  
      
311  
      if (!useGZIP) {
312  
        time = now()-start;
313  
        if (!quietSave)
314  
          print("Saving " + toM(l(s)) + "M chars (" /*+ changesWritten + ", "*/ + time + " ms)");
315  
        start = now();
316  
        saveTextFile(f, javaTokWordWrap(s));
317  
        newFile(conceptsDir(), "concepts.structure.gz").delete();
318  
      }
319  
      
320  
      File conceptsFile = conceptsFile();
321  
      File backupFile = newFile(conceptsDir(), "backups/" + fileName(conceptsFile) + ".backup" + ymd() + "-" + formatInt(hours(), 2)
322  
        + (newBackupEveryXMinutes >= 60 ? "" : formatInt(roundDownTo_rev(minutes(), newBackupEveryXMinutes), 2)));
323  
      // TODO: get rid of this
324  
      copyFile(f, backupFile);
325  
      
326  
      time = now()-start;
327  
      if (!quietSave)
328  
        print("Saved " + toK(f.length()) + " K, " + n(concepts, "concepts") + " (" + time + " ms)");
329  
      lastSaveWas = fullTime;
330  
      lastSaveTook = now()-fullTime;
331  
      saveError(null);
332  
    } finally {
333  
      savingConcepts = false;
334  
      saverLock.unlock();
335  
    }
336  
  }
337  
  
338  
  void _autoSaveConcepts() {
339  
    if (autoSaveInterval < 0 && maxAutoSavePercentage != 0) {
340  
      long pivotTime = Math.round(lastSaveWas+lastSaveTook*100.0/maxAutoSavePercentage);
341  
      if (now() < pivotTime) {
342  
        //print("Skipping auto-save (last save took " + lastSaveTook + ")");
343  
        ret;
344  
      }
345  
    }
346  
    try {
347  
      saveConcepts();
348  
    } catch e {
349  
      saveError(e);
350  
      print("Concept save failed, will try again");
351  
      printStackTrace(e);
352  
      fireSaveError(e);
353  
    }
354  
  }
355  
  
356  
  S fullStructure() {
357  
    ret structure(cloneMap(concepts), makeStructureData());
358  
  }
359  
  
360  
  swappable structure_Data makeStructureData() {
361  
    ret finishStructureData(new structure_Data);
362  
  }
363  
  
364  
  structure_Data finishStructureData(structure_Data data) {
365  
    if (storeBaseClassesInStructure)
366  
      data.storeBaseClasses = true;
367  
    ret data;
368  
  }
369  
  
370  
  void clearConcepts() {
371  
    for (Concept c : allConcepts()) c.delete();
372  
    //concepts.clear();
373  
    //allChanged();
374  
  }
375  
  
376  
  void fireLegacyChangeEvent() {
377  
    synchronized(this) { ++changes; lastChange = sysNow(); }
378  
    if (vmBusSend) vmBus_send conceptsChanged(this);
379  
    pcallFAll(onAllChanged);
380  
  }
381  
  
382  
  // auto-save every second if dirty
383  
  synchronized void autoSaveConcepts() {
384  
    if (autoSaver == null) {
385  
      if (isTransient()) fail("Can't persist transient database");
386  
      autoSaver = doEvery_daemon("Concepts Saver for " + conceptsDir(),
387  
        abs(autoSaveInterval), r { _autoSaveConcepts() });
388  
      // print("Installed auto-saver (" + autoSaveInterval + " ms, " + progID() + ")");
389  
    }
390  
  }
391  
  
392  
  public void close { cleanMeUp(); }
393  
  
394  
  void cleanMeUp() {
395  
    pcall {
396  
      if (closeConceptsOnExit)
397  
        closeAllOpt(allConcepts());
398  
        
399  
      set defunct;
400  
      bool shouldSave = autoSaver != null;
401  
      if (autoSaver != null) {
402  
        autoSaver.cancel();
403  
        autoSaver = null;
404  
      }
405  
      while (savingConcepts) sleepInCleanUp(10);
406  
      if (shouldSave)
407  
        saveConceptsIfDirty();
408  
    }
409  
    dispose fileLock;
410  
  }
411  
  
412  
  Map<Long, S> getIDsAndNames() {
413  
    new Map<Long, S> map;
414  
    Map<Long, Concept> cloned = cloneMap(concepts);
415  
    for (long id : keys(cloned)) 
416  
      map.put(id, cloned.get(id).className);
417  
    ret map;
418  
  }
419  
  
420  
  void deleteConcepts(L l) {
421  
    ping();
422  
    if (l != null) for (O o : cloneList(l))
423  
      if (o instanceof Long) {
424  
        Concept c = concepts.get(o);
425  
        if (c != null) c.delete();
426  
      } else if (o cast Concept)
427  
        o.delete();
428  
      else
429  
        warn("Can't delete " + getClassName(o));
430  
  }
431  
  
432  
  <A extends Concept> A conceptOfType(Class<A> type) {
433  
    IConceptCounter counter = conceptCounterForClass(type);
434  
    if (counter != null) ret (A) first(counter.allConcepts());
435  
    ret firstOfType(allConcepts(), type);
436  
  }
437  
  
438  
  <A extends Concept> L<A> conceptsOfType(Class<A> type) {
439  
    L<A> l = conceptsOfType_noParent(type);
440  
    if (parent == null) ret l;
441  
    ret concatLists_conservative(l, parent.conceptsOfType(type));
442  
  }
443  
  
444  
  <A extends Concept> L<A> conceptsOfType_noParent(Class<A> type) {
445  
    ping();
446  
    IConceptCounter counter = conceptCounterForClass(type);
447  
    if (counter != null) ret (L<A>) cloneList(counter.allConcepts());
448  
    ret filterByType(allConcepts(), type);
449  
  }
450  
  
451  
  <A extends Concept> L<A> listConcepts(Class<A> type) {
452  
    ret conceptsOfType(type);
453  
  }
454  
  
455  
  <A extends Concept> L<A> list(Class<A> type) {
456  
    ret conceptsOfType(type);
457  
  }
458  
  
459  
  <A extends Concept> L<A> list_noParent(Class<A> type) {
460  
    ret conceptsOfType_noParent(type);
461  
  }
462  
  
463  
  // TODO: would be better to make this Cl (indices may return sets)
464  
  L<Concept> list(S type) {
465  
    ret conceptsOfType(type);
466  
  }
467  
  
468  
  L<Concept> conceptsOfType(S type) {
469  
    ret filterByDynamicType(allConcepts(), "main$" + type);
470  
  }
471  
  
472  
  bool hasConceptOfType(Class<? extends Concept> type) {
473  
    ret hasType(allConcepts(), type);
474  
  }
475  
  
476  
  void persistConcepts() {
477  
    loadConcepts();
478  
    autoSaveConcepts();
479  
  }
480  
  
481  
  // We love synonyms
482  
  void conceptPersistence() { persistConcepts(); }
483  
  
484  
  Concepts persist() { persistConcepts(); ret this; }
485  
  void persist(Int interval) {
486  
    if (interval != null) autoSaveInterval = interval;
487  
    persist();
488  
  }
489  
    
490  
  // Runs r if there is no concept of that type
491  
  <A extends Concept> A ensureHas(Class<A> c, Runnable r) {
492  
    A a = conceptOfType(c);
493  
    if (a == null) {
494  
      r.run();
495  
      a = conceptOfType(c);
496  
      if (a == null)
497  
        fail("Concept not made by " + r + ": " + shortClassName(c));
498  
    }
499  
    ret a;
500  
  }
501  
  
502  
  // Ensures that every concept of type c1 is ref'd by a concept of
503  
  // type c2.
504  
  // Type of func: voidfunc(concept)
505  
  void ensureHas(Class<? extends Concept> c1, Class<? extends Concept> c2, O func) {
506  
    for (Concept a : conceptsOfType(c1)) {
507  
      Concept b = findBackRef(a, c2);
508  
      if (b == null) {
509  
        callF(func, a);
510  
        b = findBackRef(a, c2);
511  
        if (b == null)
512  
          fail("Concept not made by " + func + ": " + shortClassName(c2));
513  
      }
514  
    }
515  
  }
516  
  
517  
  // Type of func: voidfunc(concept)
518  
  void forEvery(Class<? extends Concept> type, O func) {
519  
    for (Concept c : conceptsOfType(type))
520  
      callF(func, c);
521  
  }
522  
  
523  
  int deleteAll(Class<? extends Concept> type) {
524  
    L<Concept> l = (L) conceptsOfType(type);
525  
    for (Concept c : l) c.delete();
526  
    ret l(l);
527  
  }
528  
  
529  
  // always returns a new list (callers depend on this)
530  
  Collection<Concept> allConcepts() {
531  
    synchronized(concepts) {
532  
      ret new L(values(concepts));
533  
    }
534  
  }
535  
  
536  
  IConceptCounter conceptCounterForClass(Class<? extends Concept> c) {
537  
    for (IFieldIndex idx : values(mapGet(fieldIndices, c)))
538  
      if (idx cast IConceptCounter) ret idx;
539  
    for (IFieldIndex idx : values(mapGet(ciFieldIndices, c)))
540  
      if (idx cast IConceptCounter) ret idx;
541  
    null;
542  
  }
543  
  
544  
545  
  <A extends Concept> int countConcepts(Class<A> c, O... params) {
546  
    int n = countConcepts_noParent(c, params);
547  
    if (parent == null) ret n;
548  
    ret n+parent.countConcepts(c, params);
549  
  }
550  
  
551  
  <A extends Concept> int countConcepts_noParent(Class<A> c, O... params) {
552  
    ping();
553  
    if (empty(params)) {
554  
      IConceptCounter counter = conceptCounterForClass(c);
555  
      if (counter != null) ret counter.countConcepts();
556  
      ret l(list_noParent(c));
557  
    }
558  
    int n = 0;
559  
    for (A x : list_noParent(c)) if (checkConceptFields(x, params)) ++n;
560  
    ret n;
561  
  }
562  
563  
  int countConcepts(S c, O... params) {
564  
    ping();
565  
    if (empty(params)) ret l(list(c));
566  
    int n = 0;
567  
    for (Concept x : list(c)) if (checkConceptFields(x, params)) ++n;
568  
    ret n;
569  
  }
570  
571  
  int countConcepts() {
572  
    ret l(concepts);
573  
  }
574  
  
575  
  synchronized L<IConceptIndex> clonedConceptIndices() {
576  
    ret cloneList(conceptIndices);
577  
  }
578  
  
579  
  synchronized void addConceptIndex(IConceptIndex index) {
580  
    if (conceptIndices == null)
581  
      conceptIndices = new L;
582  
    conceptIndices.add(index);
583  
  }
584  
  
585  
  synchronized void removeConceptIndex(IConceptIndex index) {
586  
    if (conceptIndices == null) ret;
587  
    conceptIndices.remove(index);
588  
    if (empty(conceptIndices)) conceptIndices = null;
589  
  }
590  
  
591  
  synchronized void addFieldIndex(Class<? extends Concept> c, S field, IFieldIndex index) {
592  
    if (fieldIndices == null)
593  
      fieldIndices = new HashMap;
594  
    Map<S, IFieldIndex> map = fieldIndices.get(c);
595  
    if (map == null)
596  
      fieldIndices.put(c, map = new HashMap);
597  
    map.put(field, index);
598  
  }
599  
  
600  
  synchronized void removeFieldIndex(Class<? extends Concept> c, S field, IFieldIndex index) {
601  
    Map<S, IFieldIndex> map = mapGet(fieldIndices, c);
602  
    mapRemove(map, field);
603  
  }
604  
  
605  
  synchronized IFieldIndex getFieldIndex(Class<? extends Concept> c, S field) {
606  
    if (fieldIndices == null) null;
607  
    Map<S, IFieldIndex> map = fieldIndices.get(c);
608  
    ret map == null ? null : map.get(field);
609  
  }
610  
  
611  
  synchronized IFieldIndex getAnyIndexForClass(Class<? extends Concept> c) {
612  
    ret first(allIndicesForClass(c));
613  
  }
614  
  
615  
  synchronized Cl<IFieldIndex> allIndicesForClass(Class<? extends Concept> c) {
616  
    ret concatLists(
617  
      values(fieldIndices?.get(c)),
618  
      values(ciFieldIndices?.get(c)));
619  
  }
620  
  
621  
  synchronized void addCIFieldIndex(Class<? extends Concept> c, S field, IFieldIndex index) {
622  
    if (ciFieldIndices == null)
623  
      ciFieldIndices = new HashMap;
624  
    Map<S, IFieldIndex> map = ciFieldIndices.get(c);
625  
    if (map == null)
626  
      ciFieldIndices.put(c, map = new HashMap);
627  
    map.put(field, index);
628  
  }
629  
  
630  
  synchronized void removeCIFieldIndex(Class<? extends Concept> c, S field) {
631  
    Map<S, IFieldIndex> map = mapGet(ciFieldIndices, c);
632  
    mapRemove(map, field);
633  
  }
634  
  
635  
  synchronized IFieldIndex getCIFieldIndex(Class<? extends Concept> c, S field) {
636  
    if (ciFieldIndices == null) null;
637  
    Map<S, IFieldIndex> map = ciFieldIndices.get(c);
638  
    ret map == null ? null : map.get(field);
639  
  }
640  
  
641  
  // inter-process methods
642  
  
643  
  RC xnew(S name, O... values) {
644  
    ret new RC(cnew(name, values));
645  
  }
646  
  
647  
  void xset(long id, S field, O value) {
648  
    xset(new RC(id), field, value);
649  
  }
650  
  
651  
  void xset(RC c, S field, O value) {
652  
    if (value instanceof RC)
653  
      value = getConcept((RC) value);
654  
    cset(getConcept(c), field, value);
655  
  }
656  
  
657  
  O xget(long id, S field) {
658  
    ret xget(new RC(id), field);
659  
  }
660  
  
661  
  O xget(RC c, S field) {
662  
    ret xgetPost(cget(getConcept(c), field));
663  
  }
664  
  
665  
  O xgetPost(O o) {
666  
    o = deref(o);
667  
    if (o instanceof Concept)
668  
      ret new RC((Concept) o);
669  
    ret o;
670  
  }
671  
  
672  
  void xdelete(long id) {
673  
    xdelete(new RC(id));
674  
  }
675  
  
676  
  void xdelete(RC c) {
677  
    getConcept(c).delete();
678  
  }
679  
  
680  
  void xdelete(L<RC> l) {
681  
    for (RC c : l)
682  
      xdelete(c);
683  
  }
684  
  
685  
  L<RC> xlist() {
686  
    ret map("toPassRef", allConcepts());
687  
  }
688  
  
689  
  L<RC> xlist(S className) {
690  
    ret map("toPassRef", conceptsOfType(className));
691  
  }
692  
  
693  
  bool isTransient() { ret eq(programID, "-"); }
694  
  
695  
  S xfullgrab() {
696  
    if (noXFullGrab) fail("no xfullgrab (DB too large)");
697  
    lock lock();
698  
    if (changes == changesWritten && !isTransient())
699  
      ret loadConceptsStructure(programID);
700  
    ret fullStructure();
701  
  }
702  
  
703  
  /* dev.
704  
  Either<File, byte[]> xfullgrabGZipped() {
705  
    lock lock();
706  
    if (changes == changesWritten && !isTransient())
707  
      ret loadConceptsStructure(programID);
708  
    ret fullStructure();
709  
  }*/
710  
  
711  
  void xshutdown() {
712  
    // Killing whole VM if someone wants this DB to shut down
713  
    cleanKillVM();
714  
  }
715  
  
716  
  long xchangeCount() { ret changes; }
717  
  int xcount() { ret countConcepts(); }
718  
  
719  
  void register(Concept c) {
720  
    ping();
721  
    if (c._concepts == this) ret;
722  
    if (c._concepts != null) fail("Can't re-register");
723  
    c.id = internalID();
724  
    c.created = now();
725  
    if (modifyOnCreate) c._setModified(c.created);
726  
    register_phase2(c);
727  
    vmBus_send conceptCreated(c);
728  
    fireChange(new ConceptCreate(c));
729  
  }
730  
  
731  
  // also called by replaceConceptAndUpdateRefs
732  
  void register_phase2(Concept c) {
733  
    c._concepts = this;
734  
    concepts.put((long) c.id, c);
735  
    for (Concept.Ref r : c._refs())
736  
      r.index();
737  
    c.change();
738  
    c._onRegistered();
739  
  }
740  
  
741  
  void registerKeepingID(Concept c) {
742  
    if (c._concepts == this) ret;
743  
    if (c._concepts != null) fail("Can't re-register");
744  
    c._concepts = this;
745  
    concepts.put((long) c.id, c);
746  
    c.change();
747  
  }
748  
  
749  
  void conceptChanged(Concept c) {
750  
    fireChange(new ConceptChange(c));
751  
    if (conceptIndices != null)
752  
      for (IConceptIndex index : clonedConceptIndices())
753  
        index.update(c);
754  
  }
755  
  
756  
  bool hasUnsavedData() {
757  
    ret changes != changesWritten || savingConcepts;
758  
  }
759  
  
760  
  synchronized O miscMapGet(O key) {
761  
    ret mapGet(miscMap, key);
762  
  }
763  
  
764  
  synchronized O miscMapPut(O key, O value) {
765  
    if (miscMap == null) miscMap = new Map;
766  
    ret miscMap.put(key, value);
767  
  }
768  
  
769  
  synchronized void miscMapRemove(O key) {
770  
    mapRemove(miscMap, key);
771  
  }
772  
  
773  
  // Note: auto-typing can fool you, make sure create returns
774  
  // a wide enough type
775  
  synchronized <A> A miscMapGetOrCreate(O key, IF0<A> create) {
776  
    if (containsKey(miscMap, key)) ret (A) miscMap.get(key);
777  
    A value = create!;
778  
    miscMapPut(key, value);
779  
    ret value;
780  
  }
781  
  
782  
  void setParent(Concepts parent) {
783  
    this.parent = parent;
784  
  }
785  
  
786  
  void fireChange(ConceptsChange change) {
787  
    if (change == null) ret;
788  
    pcallFAll(onChange, change);
789  
    fireLegacyChangeEvent();
790  
  }
791  
  
792  
  void addChangeListener aka onChange(IVF1<ConceptsChange> l) {
793  
    syncAdd(onChange, l);
794  
  }
795  
  
796  
  void removeChangeListener(IVF1<ConceptsChange> l) {
797  
    syncRemove(onChange, l);
798  
  }
799  
  
800  
  void addPreSave(Runnable r) {
801  
    preSave = syncAddOrCreate(preSave, r);
802  
  }
803  
  
804  
  toString {
805  
    ret nConcepts(concepts) + " (" + conceptsDir() + ", hash: " + identityHashCode(this) + ")";
806  
  }
807  
} // end of Concepts
808  
809  
// TODO: Move everyone over to this so we can have concepts that
810  
// don't subclass Concept. It's hard though because of Concept.Ref
811  
interface IConcept {
812  
  public long _conceptID();
813  
  public Concepts concepts();
814  
}
815  
816  
sclass Concept extends DynamicObject is IConcept, ChangeTriggerable {
817  
  transient Concepts _concepts; // Where we belong
818  
  long id;
819  
  long created, _modified;
820  
  ifdef ConceptRefsField
821  
  L<Ref> refs;
822  
  endifdef
823  
  L<Ref> backRefs;
824  
  
825  
  // used only internally (cnew)
826  
  *(S className) {
827  
    super(className);
828  
    _created();
829  
  }
830  
  
831  
  *() {
832  
    if (!_loading()) {
833  
      //className = shortClassName(this); // XXX - necessary?
834  
      //print("New concept of type " + className);
835  
      _created();
836  
    }
837  
  }
838  
  
839  
  *(bool unlisted) {
840  
    if (!unlisted) _created();
841  
  }
842  
  
843  
  bool includeZeroIDInToString() { false; }
844  
  
845  
  toString {
846  
    S s = shortDynamicClassName(this);
847  
    long id = this.id;
848  
    if (id != 0 || includeZeroIDInToString())
849  
      s += " " + id;
850  
    ret s;
851  
  }
852  
  
853  
  static bool loading() { ret _loading(); }
854  
  static bool _loading() { ret dynamicObjectIsLoading(); }
855  
856  
  void _created() {
857  
    if (!concepts_unlistedByDefault && !eq(concepts_unlisted!, true))
858  
      db_mainConcepts().register(this);
859  
  }
860  
  
861  
  // base class + required interface. experimental
862  
  persistable class TypedRef<A extends Concept, B> extends Ref<A> {
863  
    //Class<B> aType;
864  
    Class<B> bType;
865  
    
866  
    *(Class<B> *bType) {}
867  
    *(Class<B> *bType, B value) { set((A) value); }
868  
    *(B value) { set((A) value); }
869  
    
870  
    public bool set(A a) {
871  
      ret super.set(checkValue(a));
872  
    }
873  
    
874  
    void check { checkValue(get()); }
875  
    
876  
    <C> C checkValue(C a) {
877  
      if (bType != null && a != null)
878  
        assertIsInstance(a, bType);
879  
      ret a;
880  
    }
881  
    
882  
    B b() { ret (B) value; }
883  
  }
884  
  
885  
  class Ref<A extends Concept> implements IRef<A> {
886  
    A value;
887  
    
888  
    *() {
889  
      if (!dynamicObjectIsLoading())
890  
        registerRef();
891  
    }
892  
    
893  
    void registerRef {
894  
      vmBus_send registeringConceptRef(this);
895  
    }
896  
    
897  
    *(A *value) {
898  
      registerRef();
899  
      index();
900  
    }
901  
    
902  
    // get owning concept (source)
903  
    Concept concept() {
904  
      ret Concept.this;
905  
    }
906  
    
907  
    // get target
908  
    public A get() { ret value; }
909  
    public bool has() { ret value != null; }
910  
    
911  
    bool set(A a) {
912  
      if (a == value) false;
913  
      unindex();
914  
      value = a;
915  
      index();
916  
      change();
917  
      true;
918  
    }
919  
    
920  
    void setIfEmpty(A a) {
921  
      if (!has()) set(a);
922  
    }
923  
    
924  
    public void set(Ref<A> ref) { set(ref.get()); }
925  
    public void clear() { set((A) null); }
926  
    
927  
    bool validRef() {
928  
      ret value != null && _concepts != null && _concepts == value._concepts;
929  
    }
930  
    
931  
    // TODO: sync all the indexing and unindexing!?
932  
    void index() { 
933  
      if (validRef()) {
934  
        value._addBackRef(this);
935  
        change();
936  
      }
937  
    }
938  
939  
    Ref<A> unindex() {
940  
      if (validRef()) {
941  
        value._removeBackRef(this);
942  
        change();
943  
      }
944  
      this;
945  
    }
946  
    
947  
    void unindexAndDrop {
948  
      unindex();
949  
      _removeRef(this);
950  
    }
951  
    
952  
    void change() {
953  
      Concept.this.change();
954  
    }
955  
    
956  
    toString { ret ifdef ConceptRef_markInToString "Concept.Ref " + endifdef
957  
    str(value); }
958  
  }
959  
  
960  
  class RefL<A extends Concept> extends AbstractList<A> {
961  
    new L<Ref<A>> l;
962  
    
963  
    *() {}
964  
    *(L<A> l) { replaceWithList(l); }
965  
    
966  
    public void clear {
967  
      while (!isEmpty()) main removeLast(this);
968  
    }
969  
    
970  
    public void replaceWithList(L<A> l) {
971  
      clear();
972  
      fOr (A a : l) add(a);
973  
    }
974  
    
975  
    public A set(int i, A o) {
976  
      Ref<A> ref = syncGet(l, i);
977  
      A prev = ref!;
978  
      ref.set(o);
979  
      ret prev;
980  
    }
981  
    
982  
    public void add(int i, A o) {
983  
      syncAdd(l, i, new Ref(o));
984  
    }
985  
    
986  
    public A get(int i) {
987  
      ret syncGet(l, i)!;
988  
    }
989  
    
990  
    public A remove(int i) {
991  
      ret syncRemove(l, i)!;
992  
    }
993  
    
994  
    public int size() {
995  
      ret syncL(l);
996  
    }
997  
    
998  
    public bool contains(O o) {
999  
      if (o instanceof Concept)
1000  
        for (Ref<A> r : l) if (eq(r!, o)) true;
1001  
      ret super.contains(o);
1002  
    }
1003  
  }
1004  
  
1005  
  void delete() {
1006  
    //name = "[defunct " + name + "]";
1007  
    //defunct = true;
1008  
    //energy = 0;
1009  
    
1010  
    // clean refs
1011  
    
1012  
    fOr (Ref r : _refs())
1013  
      r.unindex();
1014  
    ifdef ConceptRefsField
1015  
    refs = null;
1016  
    endifdef
1017  
    
1018  
    // set back refs to null
1019  
    
1020  
    for (Ref r : cloneList(backRefs))
1021  
      r.set((Concept) null);
1022  
    backRefs = null;
1023  
    
1024  
    var _concepts = this._concepts;
1025  
    if (_concepts != null) {
1026  
      _concepts.concepts.remove(id);
1027  
      _concepts.fireChange(new ConceptDelete(id, this));
1028  
      if (_concepts.conceptIndices != null)
1029  
        for (IConceptIndex index : _concepts.conceptIndices)
1030  
          index.remove(this);
1031  
      this._concepts = null;
1032  
    }
1033  
    id = 0;
1034  
  }
1035  
  
1036  
  BaseXRef export() {
1037  
    ret new BaseXRef(_concepts.progID(), id);
1038  
  }
1039  
  
1040  
  // notice system of a change in this object
1041  
  public void change aka _change() {
1042  
    _setModified(now());
1043  
    _change_withoutUpdatingModifiedField();
1044  
  }
1045  
  
1046  
  void _setModified(long modified) {
1047  
    _modified = modified;
1048  
  }
1049  
  
1050  
  final void _change_withoutUpdatingModifiedField() {
1051  
    _onChange();
1052  
    if (_concepts != null) _concepts.conceptChanged(this);
1053  
  }
1054  
  
1055  
  // overridable
1056  
  void _onChange {}
1057  
  
1058  
  S _programID() {
1059  
    ret _concepts == null ? getDBProgramID() : _concepts.progID();
1060  
  }
1061  
  
1062  
  // overridable
1063  
  
1064  
  void _addBackRef(Concept.Ref ref) {
1065  
    backRefs = addDyn_quickSync(backRefs, ref);
1066  
    _backRefsModified();
1067  
  }
1068  
  
1069  
  void _backRefsModified {
1070  
    if (_concepts != null && _concepts.modifyOnBackRef) change();
1071  
  }
1072  
  
1073  
  void _removeBackRef(Concept.Ref ref) {
1074  
    backRefs = removeDyn_quickSync(backRefs, ref);
1075  
    _backRefsModified();
1076  
  }
1077  
  
1078  
  void _removeRef(Concept.Ref ref) {
1079  
    ifdef ConceptRefsField
1080  
    refs = removeDyn_quickSync(refs, ref);
1081  
    endifdef
1082  
  }
1083  
  
1084  
  int _backRefCount() { ret syncL(backRefs); }
1085  
  
1086  
  // convenience methods
1087  
  
1088  
  void _setField aka setField(S field, O value) {
1089  
    cset(this, field, value);
1090  
  }
1091  
  
1092  
  bool setField_trueIfChanged(S field, O value) {
1093  
    ret cset(this, field, value) != 0;
1094  
  }
1095  
  
1096  
  <A> A setFieldAndReturn(S field, A value) {
1097  
    setField(field, value);
1098  
    ret value;
1099  
  }
1100  
  
1101  
  void _setFields aka setFields(O... values) {
1102  
    cset(this, values);
1103  
  }
1104  
  
1105  
  public Concepts concepts() { ret _concepts; }
1106  
  
1107  
  bool isDeleted() { ret id == 0; }
1108  
  
1109  
  // Called directly after concept is unstructured
1110  
  void _doneLoading() {}
1111  
  
1112  
  // Called after all concepts are unstructured and added to Concepts
1113  
  void _doneLoading2() {
1114  
    Map<S, FieldMigration> map = _fieldMigrations();
1115  
    if (map != null) for (S oldField, FieldMigration m : map)
1116  
      crenameField_noOverwrite(this, oldField, m.newField);
1117  
  }
1118  
  
1119  
  srecord FieldMigration(S newField) {}
1120  
  
1121  
  // value is 
1122  
  Map<S, FieldMigration> _fieldMigrations() { null; }
1123  
  
1124  
  // new wrapper to get a copy of the refs list
1125  
  // so we can eventually drop the refs field
1126  
  Cl<Ref> _refs() {
1127  
    ret scanConceptForRefs(this);
1128  
  }
1129  
  
1130  
  ifdef ConceptRefsField
1131  
  // for debugging
1132  
  L<Ref> _rawRefsField() {
1133  
    ret refs;
1134  
  }
1135  
  endifdef
1136  
  
1137  
  Concepts _concepts() { ret _concepts; }
1138  
  
1139  
  bool _conceptsDefunct() { ret _concepts != null && _concepts.defunct; }
1140  
  bool _conceptsDefunctOrUnregistered() { ret _concepts == null || _concepts.defunct; }
1141  
  
1142  
  // allow refs to do magic stuff?
1143  
  void _onRegistered {
1144  
    /*for (Ref ref : _refs())
1145  
      refs._onRegistered();*/
1146  
  }
1147  
  
1148  
  !include #1031423 // Concept convenience methods
1149  
  
1150  
  File conceptsDir() {
1151  
    var concepts = concepts();
1152  
    ret concepts?.conceptsDir();
1153  
  }
1154  
  
1155  
  File fileInConceptsDir(S name) {
1156  
    var dir = conceptsDir();
1157  
    ret dir == null ?: newFile(dir, name);
1158  
  }
1159  
  
1160  
  public long _conceptID() { ret id; }
1161  
} // end of Concept
1162  
1163  
// remote reference (for inter-process communication or
1164  
// external databases). Formerly "PassRef".
1165  
// prepared for string ids if we do them later
1166  
sclass RC {
1167  
  transient O owner;
1168  
  S id;
1169  
  
1170  
  *() {} // make serialisation happy
1171  
  *(long id) { this.id = str(id); }
1172  
  *(O owner, long id) { this.id = str(id); this.owner = owner; }
1173  
  *(Concept c) { this(c.id); }
1174  
  long longID() { ret parseLong(id); }
1175  
  
1176  
  public S toString() {
1177  
    ret id;
1178  
  }
1179  
} // end of RC
1180  
1181  
// Reference to a concept in another program
1182  
sclass BaseXRef {
1183  
  S programID;
1184  
  long id;
1185  
    
1186  
  *() {}
1187  
  *(S *programID, long *id) {}
1188  
  
1189  
  public bool equals(O o) {
1190  
    if (!(o instanceof BaseXRef)) false;
1191  
    BaseXRef r = cast o;
1192  
    ret eq(programID, r.programID) && eq(id, r.id);
1193  
  }
1194  
  
1195  
  public int hashCode() {
1196  
    ret programID.hashCode() + (int) id;
1197  
  }
1198  
}
1199  
1200  
// BaseXRef as a concept
1201  
sclass XRef extends Concept {
1202  
  BaseXRef ref;
1203  
  
1204  
  *() {}
1205  
  *(BaseXRef *ref) { _doneLoading2(); }
1206  
  
1207  
  // after we have been added to concepts
1208  
  void _doneLoading2() {
1209  
    getIndex().put(ref, this);
1210  
  }
1211  
    
1212  
  HashMap<BaseXRef, XRef> getIndex() {
1213  
    ret getXRefIndex(_concepts);
1214  
  }
1215  
}
1216  
1217  
static synchronized HashMap<BaseXRef, XRef> getXRefIndex(Concepts concepts) {
1218  
  HashMap cache = (HashMap) concepts.perClassData().get(XRef.class);
1219  
  if (cache == null)
1220  
    concepts.perClassData.put(XRef.class, cache = new HashMap);
1221  
  ret cache;
1222  
}
1223  
1224  
// uses mainConcepts
1225  
static XRef lookupOrCreateXRef(BaseXRef ref) {
1226  
  XRef xref = getXRefIndex(db_mainConcepts()).get(ref);
1227  
  if (xref == null)
1228  
    xref = new XRef(ref);
1229  
  ret xref;
1230  
}
1231  
1232  
// define standard concept functions to use main concepts
1233  
1234  
// Now in db_mainConcepts()
1235  
/*static void cleanMeUp_concepts() {
1236  
  if (db_mainConcepts() != null) db_mainConcepts().cleanMeUp();
1237  
  // mainConcepts = null; // TODO
1238  
}*/
1239  
1240  
svoid loadAndAutoSaveConcepts {
1241  
  db_mainConcepts().persist();
1242  
}
1243  
1244  
svoid loadAndAutoSaveConcepts(int interval) {
1245  
  db_mainConcepts().persist(interval);
1246  
}
1247  
1248  
static RC toPassRef(Concept c) {
1249  
  ret new RC(c);
1250  
}
1251  
1252  
// so we can instantiate the program to run as a bare DB bot
1253  
please include function bareDBMode.
1254  
1255  
// so unstructuring Concepts works
1256  
please include function dynamicObjectIsLoading_threadLocal.
1257  
1258  
svoid concepts_setUnlistedByDefault(bool b) {
1259  
  concepts_unlistedByDefault = b;
1260  
}

Author comment

Began life as a copy of #1004681

download  show line numbers  debug dex  old transpilations   

Travelled to 27 computer(s): aoiabmzegqzx, ayivmpnvhhik, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, ddnzoavkxhuk, gwrvuhgaqvyk, imzmzdywqqli, irmadwmeruwu, ishqpsrjomds, jozkyjcghlvl, jtubtzbbkimh, lpdgvwnxivlt, lqnftawlhpir, lulzaavyztxj, mowyntqkapby, mqqgnosmbjvj, onxytkatvevr, ppjhyzlbdabe, pyentgdyhuwx, pzhvpgtvlbxg, sawdedvomwva, tslmcundralx, tvejysmllsmz, vouqrxazstgt, wtqryiryparv, xrpafgyirdlv

No comments. add comment

Snippet ID: #1004863
Snippet name: "Concepts" (persistent Java objects, include)
Eternal ID of this version: #1004863/349
Text MD5: 2e1aa164680de4e6f37be15138474788
Transpilation MD5: 3dd31f79c29d5581a0ec0102c2ab9b66
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2023-10-21 14:49:20
Source code size: 34898 bytes / 1260 lines
Pitched / IR pitched: No / No
Views / Downloads: 2137 / 23286
Version history: 348 change(s)
Referenced in: [show references]