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

1039
LINES

< > BotCompany Repo | #1030886 // "Concepts" [backup before parent]

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

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: #1030886
Snippet name: "Concepts" [backup before parent]
Eternal ID of this version: #1030886/1
Text MD5: 59d330fd9fb92002072ba02d22565c09
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2021-04-06 12:19:00
Source code size: 27824 bytes / 1039 lines
Pitched / IR pitched: No / No
Views / Downloads: 147 / 160
Referenced in: [show references]