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 interface Derefable {
|
7 | Concept get(); |
8 | } |
9 | |
10 | sclass Concept extends DynamicObject {
|
11 | long id; |
12 | //double energy; |
13 | //bool defunct; |
14 | long created; |
15 | |
16 | // used only internally (cnew) |
17 | *(S className) {
|
18 | super(className); |
19 | _created(); |
20 | } |
21 | |
22 | *() {
|
23 | className = shortClassName(this); |
24 | if (!DynamicObject_loading) {
|
25 | //print("New concept of type " + className);
|
26 | _created(); |
27 | } |
28 | } |
29 | |
30 | new L<Ref> refs; |
31 | new L<Ref> backRefs; |
32 | |
33 | void _created() {
|
34 | created = now(); |
35 | if (!isTrue(_unlisted.get())) register(); |
36 | } |
37 | |
38 | void put(S field, O value) {
|
39 | fieldValues.put(field, value); |
40 | change(); |
41 | } |
42 | |
43 | O get(S field) {
|
44 | ret fieldValues.get(field); |
45 | } |
46 | |
47 | class Ref<A extends Concept> implements Derefable {
|
48 | A value; |
49 | |
50 | *() {
|
51 | if (!DynamicObject_loading) refs.add(this); |
52 | } |
53 | |
54 | *(A *value) {
|
55 | refs.add(this); |
56 | index(); |
57 | } |
58 | |
59 | // get owning concept (source) |
60 | Concept concept() { ret Concept.this; }
|
61 | |
62 | // get target |
63 | public A get() { ret value; }
|
64 | |
65 | bool has() { ret value != null; }
|
66 | bool empty() { ret value == null; }
|
67 | |
68 | void set(A a) {
|
69 | if (a == value) ret; |
70 | unindex(); |
71 | value = a; |
72 | index(); |
73 | } |
74 | |
75 | void set(Ref<A> ref) { set(ref.get()); }
|
76 | |
77 | void clear() { set((A) null); }
|
78 | |
79 | void index() {
|
80 | if (value != null) |
81 | value.backRefs.add(this); |
82 | change(); |
83 | } |
84 | |
85 | void unindex() {
|
86 | if (value != null) |
87 | value.backRefs.remove(this); |
88 | } |
89 | |
90 | public S toString() {
|
91 | ret str(get()); |
92 | } |
93 | } |
94 | |
95 | class RefL<A extends Concept> extends AbstractList<A> {
|
96 | new L<Ref<A>> l; |
97 | |
98 | public A set(int i, A o) {
|
99 | A prev = l.get(i).get(); |
100 | l.get(i).set(o); |
101 | change(); |
102 | ret prev; |
103 | } |
104 | |
105 | public void add(int i, A o) {
|
106 | l.add(i, new Ref(o)); |
107 | change(); |
108 | } |
109 | |
110 | public A get(int i) {
|
111 | ret l.get(i).get(); |
112 | } |
113 | |
114 | public A remove(int i) {
|
115 | A a = l.remove(i).get(); |
116 | change(); |
117 | ret a; |
118 | } |
119 | |
120 | public int size() {
|
121 | ret l.size(); |
122 | } |
123 | |
124 | // derefs |
125 | void addAll_(L l) {
|
126 | for (O x : l) add_(x); |
127 | } |
128 | |
129 | // derefs |
130 | void add_(O o) {
|
131 | add((A) deref(o)); |
132 | } |
133 | } |
134 | |
135 | void delete() {
|
136 | if (id == 0) ret; |
137 | concepts.remove(id); |
138 | conceptsByClass.get(getClass()).remove(id); |
139 | id = 0; |
140 | //name = "[defunct " + name + "]"; |
141 | //defunct = true; |
142 | //energy = 0; |
143 | for (Ref r : refs) |
144 | r.unindex(); |
145 | refs.clear(); |
146 | change(); |
147 | } |
148 | |
149 | // register a previously unlisted concept |
150 | void register() {
|
151 | if (id != 0) ret; // already registered |
152 | id = newID(); |
153 | concepts.put(id, this); |
154 | _addToClassMap(); |
155 | change(); |
156 | } |
157 | |
158 | void _addToClassMap() {
|
159 | Class c = getClass(); |
160 | Map<Long, Concept> map = conceptsByClass.get(c); |
161 | if (map == null) {
|
162 | // we're the first object, register this class |
163 | Class superclass = c.getSuperclass(); |
164 | if (superclass != Concept) |
165 | subclasses.put(superclass, c); |
166 | conceptsByClass.put(c, map = new TreeMap); |
167 | } |
168 | map.put(id, this); |
169 | } |
170 | } // class Concept |
171 | |
172 | // for inter-process communication |
173 | // prepared for string ids if we do them later |
174 | sclass PassRef {
|
175 | S id; |
176 | |
177 | *() {} // make serialisation happy
|
178 | *(long id) { this.id = str(id); }
|
179 | *(Concept c) { this(c.id); }
|
180 | long longID() { ret parseLong(id); }
|
181 | |
182 | public S toString() {
|
183 | ret id; |
184 | } |
185 | } |
186 | |
187 | sclass Event extends Concept {}
|
188 | |
189 | // This can be set by client |
190 | static bool concepts_quietSave; |
191 | |
192 | static Map<Long, Concept> concepts = synchroTreeMap(); |
193 | static Map<Class, Map<Long, Concept>> conceptsByClass = synchroHashMap(); |
194 | static new MultiMap<Class, Class> subclasses; |
195 | |
196 | static long idCounter; |
197 | static volatile long changes = 1, changesWritten; |
198 | static volatile java.util.Timer autoSaver; |
199 | static volatile bool savingConcepts; |
200 | static new ThreadLocal<Bool> _unlisted; |
201 | static bool concepts_saveOneMore; |
202 | static S conceptsLoadedFrom; |
203 | static int concepts_internStringsLongerThan = 10; |
204 | static int concepts_autoSaveInterval = 1000; |
205 | |
206 | static long newID() {
|
207 | do {
|
208 | ++idCounter; |
209 | } while (hasConcept(idCounter)); |
210 | ret idCounter; |
211 | } |
212 | |
213 | ssvoid loadConceptsFrom(S programID) {
|
214 | conceptsLoadedFrom = programID; |
215 | clearConcepts(); |
216 | DynamicObject_loading = true; |
217 | try {
|
218 | structure_internStringsLongerThan = concepts_internStringsLongerThan; |
219 | readLocally(programID, "concepts"); |
220 | readLocally(programID, "idCounter"); |
221 | assignConceptsToUs(); |
222 | } finally {
|
223 | DynamicObject_loading = false; |
224 | } |
225 | } |
226 | |
227 | svoid assignConceptsToUs() {
|
228 | for (Concept c : values(concepts)) {
|
229 | c._addToClassMap(); |
230 | callOpt(c, "_doneLoading2"); |
231 | } |
232 | } |
233 | |
234 | ssvoid loadConcepts() {
|
235 | loadConceptsFrom(programID()); |
236 | } |
237 | |
238 | static Concept getConcept(S id) {
|
239 | ret empty(id) ? null : getConcept(parseLong(id)); |
240 | } |
241 | |
242 | static Concept getConcept(long id) {
|
243 | ret (Concept) concepts.get((long) id); |
244 | } |
245 | |
246 | static Concept getConcept(PassRef ref) {
|
247 | ret ref == null ? null : getConcept(ref.longID()); |
248 | } |
249 | |
250 | static bool hasConcept(long id) {
|
251 | ret concepts.containsKey((long) id); |
252 | } |
253 | |
254 | static bool hasConcept(Class c, O... params) {
|
255 | ret conceptWhere(c, params) != null; |
256 | } |
257 | |
258 | static Concept deleteConcept(long id) {
|
259 | Concept c = getConcept(id); |
260 | if (c == null) |
261 | print("Concept " + id + " not found");
|
262 | else |
263 | c.delete(); |
264 | ret c; |
265 | } |
266 | |
267 | // call in only one thread |
268 | static void saveConceptsIfDirty() {
|
269 | saveConceptsIfDirty(programID()); |
270 | } |
271 | |
272 | static void saveConceptsIfDirty(S programID) {
|
273 | savingConcepts = true; |
274 | try {
|
275 | S s; |
276 | synchronized(main.class) {
|
277 | bool change = changes != changesWritten; |
278 | if (!change && !concepts_saveOneMore) ret; |
279 | changesWritten = changes; |
280 | concepts_saveOneMore = change; |
281 | save(programID, "idCounter"); |
282 | s = structure(cloneMap(concepts)); |
283 | } |
284 | if (!concepts_quietSave) |
285 | print("Saving " + l(s) + " chars (" + changesWritten + ")");
|
286 | saveTextFile(getProgramFile(programID, "concepts.structure"), s); |
287 | copyFile(getProgramFile(programID, "concepts.structure"), getProgramFile(programID, "concepts.structure.backup" + ymd() + "-" + formatInt(hours(), 2))); |
288 | } finally {
|
289 | savingConcepts = false; |
290 | } |
291 | } |
292 | |
293 | static void saveConcepts() {
|
294 | saveConceptsIfDirty(); |
295 | } |
296 | |
297 | static void saveConceptsTo(S programID) {
|
298 | saveConceptsIfDirty(programID); |
299 | } |
300 | |
301 | static void clearConcepts() {
|
302 | concepts.clear(); |
303 | conceptsByClass.clear(); |
304 | subclasses.clear(); |
305 | change(); |
306 | } |
307 | |
308 | static synchronized void change() {
|
309 | ++changes; |
310 | } |
311 | |
312 | // auto-save every second if dirty |
313 | static synchronized void autoSaveConcepts() {
|
314 | if (autoSaver == null) |
315 | autoSaver = doEvery(concepts_autoSaveInterval, r { saveConcepts(); });
|
316 | } |
317 | |
318 | static void cleanMeUp() {
|
319 | if (autoSaver != null) {
|
320 | autoSaver.cancel(); |
321 | autoSaver = null; |
322 | } |
323 | while (savingConcepts) sleep(10); |
324 | saveConceptsIfDirty(); |
325 | } |
326 | |
327 | static Map<Long, S> getIDsAndNames() {
|
328 | new Map<Long, S> map; |
329 | Map<Long, Concept> cloned = cloneMap(concepts); |
330 | for (long id : keys(cloned)) |
331 | map.put(id, cloned.get(id).className); |
332 | ret map; |
333 | } |
334 | |
335 | static <A extends Concept> A findBackRef(Concept c, Class<A> type) {
|
336 | ret findBackRefOfType(c, type); |
337 | } |
338 | |
339 | static <A extends Concept> A findBackRefOfType(Concept c, Class<A> type) {
|
340 | for (Concept.Ref r : c.backRefs) |
341 | if (instanceOf(r.concept(), type)) |
342 | ret (A) r.concept(); |
343 | null; |
344 | } |
345 | |
346 | static <A extends Concept> L<A> findBackRefs(Concept c, Class<A> type) {
|
347 | new L<A> l; |
348 | for (Concept.Ref r : c.backRefs) |
349 | if (instanceOf(r.concept(), type)) |
350 | l.add((A) r.concept()); |
351 | ret l; |
352 | } |
353 | |
354 | static <A extends Concept> A conceptOfType(Class<A> type) {
|
355 | ret firstOfType(allConcepts(), type); |
356 | } |
357 | |
358 | static <A extends Concept> L<A> conceptsOfType(Class<A> type) {
|
359 | //ret filterByType(allConcepts(), type); |
360 | new L<A> l; |
361 | _collectInstances(type, l); |
362 | ret l; |
363 | } |
364 | |
365 | svoid _collectInstances(Class c, L l) {
|
366 | l.addAll(values(conceptsByClass.get(c))); |
367 | for (Class sub : subclasses.get(c)) |
368 | _collectInstances(sub, l); |
369 | } |
370 | |
371 | static <A extends Concept> L<A> listConcepts(Class<A> type) {
|
372 | ret conceptsOfType(type); |
373 | } |
374 | |
375 | static L<Concept> list(S type, O... params) {
|
376 | ret empty(params) ? conceptsOfType(type) |
377 | : conceptsWhere(type, params); |
378 | } |
379 | |
380 | static <A extends Concept> L<A> list(Class<A> type, O... params) {
|
381 | ret empty(params) ? conceptsOfType(type) |
382 | : conceptsWhere(type, params); |
383 | } |
384 | |
385 | static L<Concept> conceptsOfType(S type) {
|
386 | ret filterByDynamicType(allConcepts(), "main$" + type); |
387 | } |
388 | |
389 | static bool hasConceptOfType(Class<? extends Concept> type) {
|
390 | ret hasType(allConcepts(), type); |
391 | } |
392 | |
393 | static void persistConcepts() {
|
394 | concepts_quietSave = true; |
395 | loadConcepts(); |
396 | autoSaveConcepts(); |
397 | } |
398 | |
399 | static void loadAndAutoSaveConcepts() {
|
400 | persistConcepts(); |
401 | } |
402 | |
403 | // We love synonyms |
404 | static void conceptPersistence() {
|
405 | persistConcepts(); |
406 | } |
407 | |
408 | // Runs r if there is no concept of that type |
409 | static <A extends Concept> A ensureHas(Class<A> c, Runnable r) {
|
410 | A a = conceptOfType(c); |
411 | if (a == null) {
|
412 | r.run(); |
413 | a = conceptOfType(c); |
414 | if (a == null) |
415 | fail("Concept not made by " + r + ": " + shortClassName(c));
|
416 | } |
417 | ret a; |
418 | } |
419 | |
420 | // Ensures that every concept of type c1 is ref'd by a concept of |
421 | // type c2. |
422 | // Type of func: voidfunc(concept) |
423 | static void ensureHas(Class<? extends Concept> c1, Class<? extends Concept> c2, O func) {
|
424 | for (Concept a : conceptsOfType(c1)) {
|
425 | Concept b = findBackRef(a, c2); |
426 | if (b == null) {
|
427 | callF(func, a); |
428 | b = findBackRef(a, c2); |
429 | if (b == null) |
430 | fail("Concept not made by " + func + ": " + shortClassName(c2));
|
431 | } |
432 | } |
433 | } |
434 | |
435 | // Type of func: voidfunc(concept) |
436 | static void forEvery(Class<? extends Concept> type, O func) {
|
437 | for (Concept c : conceptsOfType(type)) |
438 | callF(func, c); |
439 | } |
440 | |
441 | static int deleteAll(Class<? extends Concept> type) {
|
442 | L<Concept> l = (L) conceptsOfType(type); |
443 | for (Concept c : l) c.delete(); |
444 | ret l(l); |
445 | } |
446 | |
447 | static Collection<Concept> allConcepts() {
|
448 | synchronized(concepts) {
|
449 | ret new L(values(concepts)); |
450 | } |
451 | } |
452 | |
453 | static Concept cnew(S name, O... values) {
|
454 | Class<? extends Concept> cc = findClass(name); |
455 | Concept c = cc != null ? nuObject(cc) : new Concept(name); |
456 | csetAll(c, values); |
457 | ret c; |
458 | } |
459 | |
460 | static <A extends Concept> A cnew(Class<A> cc, O... values) {
|
461 | A c = nuObject(cc); |
462 | csetAll(c, values); |
463 | ret c; |
464 | } |
465 | |
466 | static void csetAll(Concept c, O... values) {
|
467 | cset(c, values); |
468 | } |
469 | |
470 | static void cset(Concept c, O... values) ctex {
|
471 | values = expandParams(c.getClass(), values); |
472 | warnIfOddCount(values); |
473 | for (int i = 0; i+1 < l(values); i += 2) {
|
474 | S field = (S) values[i]; |
475 | O value = values[i+1]; |
476 | Field f = setOpt_findField(c.getClass(), field); |
477 | //print("cset: " + c.id + " " + field + " " + struct(value) + " " + f);
|
478 | if (value instanceof PassRef) value = getConcept((PassRef) value); |
479 | value = deref(value); |
480 | |
481 | if (value instanceof S && l((S) value) >= concepts_internStringsLongerThan) value = ((S) value).intern(); |
482 | |
483 | if (f == null) |
484 | c.fieldValues.put(field, value instanceof Concept ? c.new Ref((Concept) value) : value); |
485 | else if (isSubtypeOf(f.getType(), Concept.Ref.class)) |
486 | ((Concept.Ref) f.get(c)).set((Concept) value); |
487 | else |
488 | f.set(c, value); |
489 | } |
490 | change(); |
491 | } |
492 | |
493 | static O cget(Concept c, S field) {
|
494 | O o = getOptDyn(c, field); |
495 | if (o instanceof Concept.Ref) ret ((Concept.Ref) o).get(); |
496 | ret o; |
497 | } |
498 | |
499 | static PassRef toPassRef(Concept c) {
|
500 | ret new PassRef(c); |
501 | } |
502 | |
503 | // inter-process methods |
504 | |
505 | static PassRef xnew(S name, O... values) {
|
506 | ret new PassRef(cnew(name, values)); |
507 | } |
508 | |
509 | static void xset(long id, S field, O value) {
|
510 | xset(new PassRef(id), field, value); |
511 | } |
512 | |
513 | static void xset(PassRef c, S field, O value) {
|
514 | if (value instanceof PassRef) |
515 | value = getConcept((PassRef) value); |
516 | cset(getConcept(c), field, value); |
517 | } |
518 | |
519 | // get class name of concept |
520 | static O xclass(PassRef id) {
|
521 | Concept c = getConcept(id); |
522 | ret c == null ? null : c.className; |
523 | } |
524 | |
525 | static O xget(long id, S field) {
|
526 | ret xget(new PassRef(id), field); |
527 | } |
528 | |
529 | static O xget(PassRef c, S field) {
|
530 | ret export(cget(getConcept(c), field)); |
531 | } |
532 | |
533 | static void xdelete(long id) {
|
534 | xdelete(new PassRef(id)); |
535 | } |
536 | |
537 | static void xdelete(PassRef c) {
|
538 | getConcept(c).delete(); |
539 | } |
540 | |
541 | static L<PassRef> xlist() {
|
542 | ret map("toPassRef", allConcepts());
|
543 | } |
544 | |
545 | static L<PassRef> xlist(S className) {
|
546 | ret map("toPassRef", conceptsOfType(className));
|
547 | } |
548 | |
549 | // end of IPC methods |
550 | |
551 | // make concept instance that is not connected to DB |
552 | static <A extends Concept> A unlisted(Class<A> c) {
|
553 | _unlisted.set(true); |
554 | try {
|
555 | ret nuObject(c); |
556 | } finally {
|
557 | _unlisted.set(null); |
558 | } |
559 | } |
560 | |
561 | static <A extends Concept> A unary(Class<A> c) {
|
562 | A a = conceptOfType(c); |
563 | if (a == null) |
564 | a = nuObject(c); |
565 | ret a; |
566 | } |
567 | |
568 | static Android3 makeDBBot(S name) {
|
569 | ret makeBot(name, makeDBResponder()); |
570 | } |
571 | |
572 | static L<S> exposedDBMethods = ll("xlist", "xnew", "xset", "xdelete", "xget", "xclass");
|
573 | |
574 | static O makeDBResponder() {
|
575 | ret new O {
|
576 | S answer(S s) {
|
577 | ret exposeMethods(s, exposedDBMethods); |
578 | } |
579 | }; |
580 | } |
581 | |
582 | static O export(O o) {
|
583 | if (o instanceof Concept) |
584 | ret new PassRef(((Concept) o).id); |
585 | ret o; |
586 | } |
587 | |
588 | ssvoid saveConceptsBack() {
|
589 | saveConceptsTo(assertNotNull(conceptsLoadedFrom)); |
590 | } |
591 | |
592 | static bool eq(O a, O b) {
|
593 | ret _eq(deref(a), deref(b)); |
594 | } |
download show line numbers debug dex old transpilations
Travelled to 15 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, ddnzoavkxhuk, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
| Snippet ID: | #1004681 |
| Snippet name: | OLD Concepts (include) |
| Eternal ID of this version: | #1004681/3 |
| Text MD5: | 6d12e262917c4a94929783915cae1ab2 |
| Author: | stefan |
| Category: | javax |
| Type: | JavaX fragment (include) |
| Public (visible to everyone): | Yes |
| Archived (hidden from active list): | No |
| Created/modified: | 2017-08-16 19:38:25 |
| Source code size: | 13914 bytes / 594 lines |
| Pitched / IR pitched: | No / No |
| Views / Downloads: | 1306 / 4727 |
| Version history: | 2 change(s) |
| Referenced in: | [show references] |