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: | 1051 / 4462 |
Version history: | 2 change(s) |
Referenced in: | [show references] |