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

1085
LINES

< > BotCompany Repo | #1031070 // "Concepts" [backup before new changes system]

JavaX fragment (include)

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

Author comment

Began life as a copy of #1004863

download  show line numbers  debug dex  old transpilations   

Travelled to 4 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, vouqrxazstgt

No comments. add comment

Snippet ID: #1031070
Snippet name: "Concepts" [backup before new changes system]
Eternal ID of this version: #1031070/1
Text MD5: 556ed47d1c223c00e44416bfa6454d5d
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2021-04-26 23:17:08
Source code size: 29318 bytes / 1085 lines
Pitched / IR pitched: No / No
Views / Downloads: 226 / 248
Referenced in: [show references]