1 | // A concept should be an object, not just a string. |
2 | |
3 | sclass Scenario { |
4 | Map<Long, Concept> concepts = synchroTreeMap(); |
5 | new HashMap<Class, O> perClassData; |
6 | S programID; |
7 | long idCounter; |
8 | volatile long changes = 1, changesWritten; |
9 | volatile java.util.Timer autoSaver; |
10 | volatile bool savingConcepts; |
11 | |
12 | *() { } |
13 | *(S *programID) {} |
14 | |
15 | long newID() { |
16 | do { |
17 | ++idCounter; |
18 | } while (hasConcept(idCounter)); |
19 | ret idCounter; |
20 | } |
21 | |
22 | synchronized void loadConceptsFrom(S programID) { |
23 | clearConcepts(); |
24 | DynamicObject_loading = true; |
25 | try { |
26 | readLocally2(this, programID, "concepts"); |
27 | assignConceptsToUs(); |
28 | readLocally2(this, programID, "idCounter"); |
29 | } finally { |
30 | DynamicObject_loading = false; |
31 | } |
32 | } |
33 | |
34 | void assignConceptsToUs() { |
35 | for (Concept c : values(concepts)) { |
36 | c.scenario = this; |
37 | callOpt(c, "_doneLoading2"); |
38 | } |
39 | } |
40 | |
41 | synchronized void loadConcepts() { |
42 | loadConceptsFrom(progID()); |
43 | } |
44 | |
45 | S progID() { |
46 | ret programID == null ? programID() : programID; |
47 | } |
48 | |
49 | Concept getConcept(S id) { |
50 | ret empty(id) ? null : getConcept(parseLong(id)); |
51 | } |
52 | |
53 | Concept getConcept(long id) { |
54 | ret (Concept) concepts.get((long) id); |
55 | } |
56 | |
57 | Concept getConcept(PassRef ref) { |
58 | ret ref == null ? null : getConcept(ref.longID()); |
59 | } |
60 | |
61 | bool hasConcept(long id) { |
62 | ret concepts.containsKey((long) id); |
63 | } |
64 | |
65 | void deleteConcept(long id) { |
66 | Concept c = getConcept(id); |
67 | if (c == null) |
68 | print("Concept " + id + " not found"); |
69 | else |
70 | c.delete(); |
71 | } |
72 | |
73 | // call in only one thread |
74 | void saveConceptsIfDirty() { |
75 | savingConcepts = true; |
76 | try { |
77 | S s; |
78 | synchronized(main.class) { |
79 | if (changes == changesWritten) ret; |
80 | changesWritten = changes; |
81 | saveLocally2(this, "idCounter"); |
82 | s = structure(cloneMap(concepts)); |
83 | } |
84 | print("Saving " + l(s) + " chars (" + changesWritten + ")"); |
85 | saveTextFile(getProgramFile("concepts.structure"), s); |
86 | copyFile(getProgramFile("concepts.structure"), getProgramFile("concepts.structure.backup" + ymd() + "-" + formatInt(hours(), 2))); |
87 | } finally { |
88 | savingConcepts = false; |
89 | } |
90 | } |
91 | |
92 | void saveConcepts() { |
93 | saveConceptsIfDirty(); |
94 | } |
95 | |
96 | void clearConcepts() { |
97 | concepts.clear(); |
98 | change(); |
99 | } |
100 | |
101 | synchronized void change() { |
102 | ++changes; |
103 | } |
104 | |
105 | // auto-save every second if dirty |
106 | synchronized void autoSaveConcepts() { |
107 | if (autoSaver == null) |
108 | autoSaver = doEvery(1000, r { saveConcepts(); }); |
109 | } |
110 | |
111 | void cleanMeUp() { |
112 | if (autoSaver != null) { |
113 | autoSaver.cancel(); |
114 | autoSaver = null; |
115 | } |
116 | while (savingConcepts) sleep(10); |
117 | saveConceptsIfDirty(); |
118 | } |
119 | |
120 | Map<Long, S> getIDsAndNames() { |
121 | new Map<Long, S> map; |
122 | Map<Long, Concept> cloned = cloneMap(concepts); |
123 | for (long id : keys(cloned)) |
124 | map.put(id, cloned.get(id).className); |
125 | ret map; |
126 | } |
127 | |
128 | void deleteConcepts(L<Long> ids) { |
129 | concepts.keySet().removeAll(ids); |
130 | } |
131 | |
132 | <A extends Concept> A findBackRef(Concept c, Class<A> type) { |
133 | ret findBackRefOfType(c, type); |
134 | } |
135 | |
136 | <A extends Concept> A findBackRefOfType(Concept c, Class<A> type) { |
137 | for (Concept.Ref r : c.backRefs) |
138 | if (instanceOf(r.concept(), type)) |
139 | ret (A) r.concept(); |
140 | null; |
141 | } |
142 | |
143 | <A extends Concept> L<A> findBackRefs(Concept c, Class<A> type) { |
144 | new L<A> l; |
145 | for (Concept.Ref r : c.backRefs) |
146 | if (instanceOf(r.concept(), type)) |
147 | l.add((A) r.concept()); |
148 | ret l; |
149 | } |
150 | |
151 | <A extends Concept> A conceptOfType(Class<A> type) { |
152 | ret firstOfType(allConcepts(), type); |
153 | } |
154 | |
155 | <A extends Concept> L<A> conceptsOfType(Class<A> type) { |
156 | ret filterByType(allConcepts(), type); |
157 | } |
158 | |
159 | <A extends Concept> L<A> listConcepts(Class<A> type) { |
160 | ret conceptsOfType(type); |
161 | } |
162 | |
163 | <A extends Concept> L<A> list(Class<A> type) { |
164 | ret conceptsOfType(type); |
165 | } |
166 | |
167 | L<Concept> conceptsOfType(S type) { |
168 | ret filterByDynamicType(allConcepts(), "main$" + type); |
169 | } |
170 | |
171 | bool hasConceptOfType(Class<? extends Concept> type) { |
172 | ret hasType(allConcepts(), type); |
173 | } |
174 | |
175 | void persistConcepts() { |
176 | loadConcepts(); |
177 | autoSaveConcepts(); |
178 | } |
179 | |
180 | // We love synonyms |
181 | void conceptPersistence() { |
182 | persistConcepts(); |
183 | } |
184 | |
185 | // Runs r if there is no concept of that type |
186 | <A extends Concept> A ensureHas(Class<A> c, Runnable r) { |
187 | A a = conceptOfType(c); |
188 | if (a == null) { |
189 | r.run(); |
190 | a = conceptOfType(c); |
191 | if (a == null) |
192 | fail("Concept not made by " + r + ": " + shortClassName(c)); |
193 | } |
194 | ret a; |
195 | } |
196 | |
197 | // Ensures that every concept of type c1 is ref'd by a concept of |
198 | // type c2. |
199 | // Type of func: voidfunc(concept) |
200 | void ensureHas(Class<? extends Concept> c1, Class<? extends Concept> c2, O func) { |
201 | for (Concept a : conceptsOfType(c1)) { |
202 | Concept b = findBackRef(a, c2); |
203 | if (b == null) { |
204 | callF(func, a); |
205 | b = findBackRef(a, c2); |
206 | if (b == null) |
207 | fail("Concept not made by " + func + ": " + shortClassName(c2)); |
208 | } |
209 | } |
210 | } |
211 | |
212 | // Type of func: voidfunc(concept) |
213 | void forEvery(Class<? extends Concept> type, O func) { |
214 | for (Concept c : conceptsOfType(type)) |
215 | callF(func, c); |
216 | } |
217 | |
218 | int deleteAll(Class<? extends Concept> type) { |
219 | L<Concept> l = (L) conceptsOfType(type); |
220 | for (Concept c : l) c.delete(); |
221 | ret l(l); |
222 | } |
223 | |
224 | Collection<Concept> allConcepts() { |
225 | synchronized(concepts) { |
226 | ret new L(values(concepts)); |
227 | } |
228 | } |
229 | |
230 | Concept cnew(S name, O... values) { |
231 | Class<? extends Concept> cc = findClass(name); |
232 | Concept c = cc != null ? nuObject(cc) : new Concept(name); |
233 | csetAll(c, values); |
234 | ret c; |
235 | } |
236 | |
237 | void csetAll(Concept c, O... values) { |
238 | for (int i = 0; i+1 < l(values); i += 2) |
239 | cset(c, (S) values[i], values[i+1]); |
240 | } |
241 | |
242 | void cset(Concept c, S field, O value) ctex { |
243 | Field f = setOpt_findField(c.getClass(), field); |
244 | print("cset: " + c.id + " " + field + " " + struct(value) + " " + f); |
245 | if (value instanceof PassRef) value = getConcept((PassRef) value); |
246 | if (f == null) |
247 | c.fieldValues.put(field, value instanceof Concept ? c.new Ref((Concept) value) : value); |
248 | else if (isSubtypeOf(f.getType(), Concept.Ref.class)) |
249 | ((Concept.Ref) f.get(c)).set((Concept) value); |
250 | else |
251 | f.set(c, value); |
252 | change(); |
253 | } |
254 | |
255 | O cget(Concept c, S field) { |
256 | O o = getOpt(c, field); |
257 | if (o instanceof Concept.Ref) ret ((Concept.Ref) o).get(); |
258 | ret o; |
259 | } |
260 | |
261 | PassRef toPassRef(Concept c) { |
262 | ret new PassRef(c); |
263 | } |
264 | |
265 | // inter-process methods |
266 | |
267 | PassRef xnew(S name, O... values) { |
268 | ret new PassRef(cnew(name, values)); |
269 | } |
270 | |
271 | void xset(long id, S field, O value) { |
272 | xset(new PassRef(id), field, value); |
273 | } |
274 | |
275 | void xset(PassRef c, S field, O value) { |
276 | if (value instanceof PassRef) |
277 | value = getConcept((PassRef) value); |
278 | cset(getConcept(c), field, value); |
279 | } |
280 | |
281 | O xget(long id, S field) { |
282 | ret xget(new PassRef(id), field); |
283 | } |
284 | |
285 | O xget(PassRef c, S field) { |
286 | ret cget(getConcept(c), field); |
287 | } |
288 | |
289 | void xdelete(long id) { |
290 | xdelete(new PassRef(id)); |
291 | } |
292 | |
293 | void xdelete(PassRef c) { |
294 | getConcept(c).delete(); |
295 | } |
296 | |
297 | L<PassRef> xlist() { |
298 | ret map("toPassRef", allConcepts()); |
299 | } |
300 | |
301 | L<PassRef> xlist(S className) { |
302 | ret map("toPassRef", conceptsOfType(className)); |
303 | } |
304 | } |
305 | |
306 | static volatile new Scenario mainScenario; // Where we create new concepts |
307 | |
308 | sclass Concept extends DynamicObject { |
309 | transient Scenario scenario; // Where we belong |
310 | long id; |
311 | O madeBy; |
312 | //double energy; |
313 | //bool defunct; |
314 | long created; |
315 | |
316 | // used only internally (cnew) |
317 | *(S className) { |
318 | super(className); |
319 | _created(); |
320 | } |
321 | |
322 | *() { |
323 | if (!DynamicObject_loading) { |
324 | className = shortClassName(this); |
325 | print("New concept of type " + className); |
326 | _created(); |
327 | } |
328 | } |
329 | |
330 | new L<Ref> refs; |
331 | new L<Ref> backRefs; |
332 | |
333 | void _created() { |
334 | scenario = mainScenario; |
335 | id = scenario.newID(); |
336 | created = now(); |
337 | scenario.concepts.put((long) id, this); |
338 | scenario.change(); |
339 | } |
340 | |
341 | void put(S field, O value) { |
342 | fieldValues.put(field, value); |
343 | scenario.change(); |
344 | } |
345 | |
346 | O get(S field) { |
347 | ret fieldValues.get(field); |
348 | } |
349 | |
350 | class Ref<A extends Concept> { |
351 | A value; |
352 | |
353 | *() { |
354 | if (!DynamicObject_loading) refs.add(this); |
355 | } |
356 | |
357 | *(A *value) { |
358 | refs.add(this); |
359 | index(); |
360 | } |
361 | |
362 | // get owning concept (source) |
363 | Concept concept() { |
364 | ret Concept.this; |
365 | } |
366 | |
367 | // get target |
368 | A get() { |
369 | ret value; |
370 | } |
371 | |
372 | void set(A a) { |
373 | if (a == value) ret; |
374 | unindex(); |
375 | value = a; |
376 | index(); |
377 | } |
378 | |
379 | void set(Ref<A> ref) { |
380 | set(ref.get()); |
381 | } |
382 | |
383 | void index() { |
384 | if (value != null) |
385 | value.backRefs.add(this); |
386 | scenario.change(); |
387 | } |
388 | |
389 | void unindex() { |
390 | if (value != null) |
391 | value.backRefs.remove(this); |
392 | } |
393 | } |
394 | |
395 | class RefL<A extends Concept> extends AbstractList<Concept> { |
396 | new L<Ref<A>> l; |
397 | |
398 | public A set(int i, A o) { |
399 | A prev = l.get(i).get(); |
400 | l.get(i).set(o); |
401 | ret prev; |
402 | } |
403 | |
404 | public void add(int i, A o) { |
405 | l.add(i, new Ref(o)); |
406 | } |
407 | |
408 | public A get(int i) { |
409 | ret l.get(i).get(); |
410 | } |
411 | |
412 | public A remove(int i) { |
413 | ret l.remove(i).get(); |
414 | } |
415 | |
416 | public int size() { |
417 | ret l.size(); |
418 | } |
419 | } |
420 | |
421 | void delete() { |
422 | scenario.concepts.remove((long) id); |
423 | id = 0; |
424 | //name = "[defunct " + name + "]"; |
425 | //defunct = true; |
426 | //energy = 0; |
427 | for (Ref r : refs) |
428 | r.unindex(); |
429 | refs.clear(); |
430 | scenario.change(); |
431 | } |
432 | |
433 | BaseXRef export() { |
434 | ret new BaseXRef(scenario.programID, id); |
435 | } |
436 | |
437 | // notice system of a change in this object |
438 | void change() { |
439 | scenario.change(); |
440 | } |
441 | } // class Concept |
442 | |
443 | // for inter-process communication |
444 | // prepared for string ids if we do them later |
445 | sclass PassRef { |
446 | S id; |
447 | |
448 | *() {} // make serialisation happy |
449 | *(long id) { this.id = str(id); } |
450 | *(Concept c) { this(c.id); } |
451 | long longID() { ret parseLong(id); } |
452 | |
453 | public S toString() { |
454 | ret id; |
455 | } |
456 | } |
457 | |
458 | sclass Event extends Concept {} |
459 | |
460 | // Reference to a concept in another program |
461 | sclass BaseXRef { |
462 | S programID; |
463 | long id; |
464 | |
465 | *() {} |
466 | *(S *programID, long *id) {} |
467 | |
468 | public bool equals(O o) { |
469 | if (!(o instanceof BaseXRef)) false; |
470 | BaseXRef r = cast o; |
471 | ret eq(programID, r.programID) && eq(id, r.id); |
472 | } |
473 | |
474 | public int hashCode() { |
475 | ret programID.hashCode() + (int) id; |
476 | } |
477 | } |
478 | |
479 | // BaseXRef as a concept |
480 | sclass XRef extends Concept { |
481 | BaseXRef ref; |
482 | |
483 | *() {} |
484 | *(BaseXRef *ref) { _doneLoading2(); } |
485 | |
486 | // after we have been added to scenario |
487 | void _doneLoading2() { |
488 | getIndex().put(ref, this); |
489 | } |
490 | |
491 | HashMap<BaseXRef, XRef> getIndex() { |
492 | ret getXRefIndex(scenario); |
493 | } |
494 | } |
495 | |
496 | static synchronized HashMap<BaseXRef, XRef> getXRefIndex(Scenario scenario) { |
497 | HashMap cache = (HashMap) scenario.perClassData.get(XRef.class); |
498 | if (cache == null) |
499 | scenario.perClassData.put(XRef.class, cache = new HashMap); |
500 | ret cache; |
501 | } |
502 | |
503 | // uses mainScenario |
504 | static XRef lookupOrCreateXRef(BaseXRef ref) { |
505 | XRef xref = getXRefIndex(mainScenario).get(ref); |
506 | if (xref == null) |
507 | xref = new XRef(ref); |
508 | ret xref; |
509 | } |
510 | |
511 | svoid loadAndAutoSaveConcepts { |
512 | mainScenario.loadConcepts(); |
513 | mainScenario.autoSaveConcepts(); |
514 | } |
Began life as a copy of #1004863
download show line numbers debug dex old transpilations
Travelled to 14 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, ddnzoavkxhuk, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1005277 |
Snippet name: | Scenarios (old, now called "concepts") |
Eternal ID of this version: | #1005277/2 |
Text MD5: | fb0fb8901c4eef87e5da57209580e32f |
Author: | stefan |
Category: | javax |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2019-07-18 13:55:17 |
Source code size: | 11918 bytes / 514 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 532 / 740 |
Version history: | 1 change(s) |
Referenced in: | [show references] |