// A concept should be an object, not just a string.
sclass Scenario {
Map<Long, Concept> concepts = synchroTreeMap();
new HashMap<Class, O> perClassData;
S programID;
long idCounter;
volatile long changes = 1, changesWritten;
volatile java.util.Timer autoSaver;
volatile bool savingConcepts;
*() { }
*(S *programID) {}
long newID() {
do {
++idCounter;
} while (hasConcept(idCounter));
ret idCounter;
}
synchronized void loadConceptsFrom(S programID) {
clearConcepts();
DynamicObject_loading = true;
try {
readLocally2(this, programID, "concepts");
assignConceptsToUs();
readLocally2(this, programID, "idCounter");
} finally {
DynamicObject_loading = false;
}
}
void assignConceptsToUs() {
for (Concept c : values(concepts)) {
c.scenario = this;
callOpt(c, "_doneLoading2");
}
}
synchronized void loadConcepts() {
loadConceptsFrom(progID());
}
S progID() {
ret programID == null ? programID() : programID;
}
Concept getConcept(S id) {
ret empty(id) ? null : getConcept(parseLong(id));
}
Concept getConcept(long id) {
ret (Concept) concepts.get((long) id);
}
Concept getConcept(PassRef ref) {
ret ref == null ? null : getConcept(ref.longID());
}
bool hasConcept(long id) {
ret concepts.containsKey((long) id);
}
void deleteConcept(long id) {
Concept c = getConcept(id);
if (c == null)
print("Concept " + id + " not found");
else
c.delete();
}
// call in only one thread
void saveConceptsIfDirty() {
savingConcepts = true;
try {
S s;
synchronized(main.class) {
if (changes == changesWritten) ret;
changesWritten = changes;
saveLocally2(this, "idCounter");
s = structure(cloneMap(concepts));
}
print("Saving " + l(s) + " chars (" + changesWritten + ")");
saveTextFile(getProgramFile("concepts.structure"), s);
copyFile(getProgramFile("concepts.structure"), getProgramFile("concepts.structure.backup" + ymd() + "-" + formatInt(hours(), 2)));
} finally {
savingConcepts = false;
}
}
void saveConcepts() {
saveConceptsIfDirty();
}
void clearConcepts() {
concepts.clear();
change();
}
synchronized void change() {
++changes;
}
// auto-save every second if dirty
synchronized void autoSaveConcepts() {
if (autoSaver == null)
autoSaver = doEvery(1000, r { saveConcepts(); });
}
void cleanMeUp() {
if (autoSaver != null) {
autoSaver.cancel();
autoSaver = null;
}
while (savingConcepts) sleep(10);
saveConceptsIfDirty();
}
Map<Long, S> getIDsAndNames() {
new Map<Long, S> map;
Map<Long, Concept> cloned = cloneMap(concepts);
for (long id : keys(cloned))
map.put(id, cloned.get(id).className);
ret map;
}
void deleteConcepts(L<Long> ids) {
concepts.keySet().removeAll(ids);
}
<A extends Concept> A findBackRef(Concept c, Class<A> type) {
ret findBackRefOfType(c, type);
}
<A extends Concept> A findBackRefOfType(Concept c, Class<A> type) {
for (Concept.Ref r : c.backRefs)
if (instanceOf(r.concept(), type))
ret (A) r.concept();
null;
}
<A extends Concept> L<A> findBackRefs(Concept c, Class<A> type) {
new L<A> l;
for (Concept.Ref r : c.backRefs)
if (instanceOf(r.concept(), type))
l.add((A) r.concept());
ret l;
}
<A extends Concept> A conceptOfType(Class<A> type) {
ret firstOfType(allConcepts(), type);
}
<A extends Concept> L<A> conceptsOfType(Class<A> type) {
ret filterByType(allConcepts(), type);
}
<A extends Concept> L<A> listConcepts(Class<A> type) {
ret conceptsOfType(type);
}
<A extends Concept> L<A> list(Class<A> type) {
ret conceptsOfType(type);
}
L<Concept> conceptsOfType(S type) {
ret filterByDynamicType(allConcepts(), "main$" + type);
}
bool hasConceptOfType(Class<? extends Concept> type) {
ret hasType(allConcepts(), type);
}
void persistConcepts() {
loadConcepts();
autoSaveConcepts();
}
// We love synonyms
void conceptPersistence() {
persistConcepts();
}
// Runs r if there is no concept of that type
<A extends Concept> A ensureHas(Class<A> c, Runnable r) {
A a = conceptOfType(c);
if (a == null) {
r.run();
a = conceptOfType(c);
if (a == null)
fail("Concept not made by " + r + ": " + shortClassName(c));
}
ret a;
}
// Ensures that every concept of type c1 is ref'd by a concept of
// type c2.
// Type of func: voidfunc(concept)
void ensureHas(Class<? extends Concept> c1, Class<? extends Concept> c2, O func) {
for (Concept a : conceptsOfType(c1)) {
Concept b = findBackRef(a, c2);
if (b == null) {
callF(func, a);
b = findBackRef(a, c2);
if (b == null)
fail("Concept not made by " + func + ": " + shortClassName(c2));
}
}
}
// Type of func: voidfunc(concept)
void forEvery(Class<? extends Concept> type, O func) {
for (Concept c : conceptsOfType(type))
callF(func, c);
}
int deleteAll(Class<? extends Concept> type) {
L<Concept> l = (L) conceptsOfType(type);
for (Concept c : l) c.delete();
ret l(l);
}
Collection<Concept> allConcepts() {
synchronized(concepts) {
ret new L(values(concepts));
}
}
Concept cnew(S name, O... values) {
Class<? extends Concept> cc = findClass(name);
Concept c = cc != null ? nuObject(cc) : new Concept(name);
csetAll(c, values);
ret c;
}
void csetAll(Concept c, O... values) {
for (int i = 0; i+1 < l(values); i += 2)
cset(c, (S) values[i], values[i+1]);
}
void cset(Concept c, S field, O value) ctex {
Field f = setOpt_findField(c.getClass(), field);
print("cset: " + c.id + " " + field + " " + struct(value) + " " + f);
if (value instanceof PassRef) value = getConcept((PassRef) value);
if (f == null)
c.fieldValues.put(field, value instanceof Concept ? c.new Ref((Concept) value) : value);
else if (isSubtypeOf(f.getType(), Concept.Ref.class))
((Concept.Ref) f.get(c)).set((Concept) value);
else
f.set(c, value);
change();
}
O cget(Concept c, S field) {
O o = getOpt(c, field);
if (o instanceof Concept.Ref) ret ((Concept.Ref) o).get();
ret o;
}
PassRef toPassRef(Concept c) {
ret new PassRef(c);
}
// inter-process methods
PassRef xnew(S name, O... values) {
ret new PassRef(cnew(name, values));
}
void xset(long id, S field, O value) {
xset(new PassRef(id), field, value);
}
void xset(PassRef c, S field, O value) {
if (value instanceof PassRef)
value = getConcept((PassRef) value);
cset(getConcept(c), field, value);
}
O xget(long id, S field) {
ret xget(new PassRef(id), field);
}
O xget(PassRef c, S field) {
ret cget(getConcept(c), field);
}
void xdelete(long id) {
xdelete(new PassRef(id));
}
void xdelete(PassRef c) {
getConcept(c).delete();
}
L<PassRef> xlist() {
ret map("toPassRef", allConcepts());
}
L<PassRef> xlist(S className) {
ret map("toPassRef", conceptsOfType(className));
}
}
static volatile new Scenario mainScenario; // Where we create new concepts
sclass Concept extends DynamicObject {
transient Scenario scenario; // Where we belong
long id;
O madeBy;
//double energy;
//bool defunct;
long created;
// used only internally (cnew)
*(S className) {
super(className);
_created();
}
*() {
if (!DynamicObject_loading) {
className = shortClassName(this);
print("New concept of type " + className);
_created();
}
}
new L<Ref> refs;
new L<Ref> backRefs;
void _created() {
scenario = mainScenario;
id = scenario.newID();
created = now();
scenario.concepts.put((long) id, this);
scenario.change();
}
void put(S field, O value) {
fieldValues.put(field, value);
scenario.change();
}
O get(S field) {
ret fieldValues.get(field);
}
class Ref<A extends Concept> {
A value;
*() {
if (!DynamicObject_loading) refs.add(this);
}
*(A *value) {
refs.add(this);
index();
}
// get owning concept (source)
Concept concept() {
ret Concept.this;
}
// get target
A get() {
ret value;
}
void set(A a) {
if (a == value) ret;
unindex();
value = a;
index();
}
void set(Ref<A> ref) {
set(ref.get());
}
void index() {
if (value != null)
value.backRefs.add(this);
scenario.change();
}
void unindex() {
if (value != null)
value.backRefs.remove(this);
}
}
class RefL<A extends Concept> extends AbstractList<Concept> {
new L<Ref<A>> l;
public A set(int i, A o) {
A prev = l.get(i).get();
l.get(i).set(o);
ret prev;
}
public void add(int i, A o) {
l.add(i, new Ref(o));
}
public A get(int i) {
ret l.get(i).get();
}
public A remove(int i) {
ret l.remove(i).get();
}
public int size() {
ret l.size();
}
}
void delete() {
scenario.concepts.remove((long) id);
id = 0;
//name = "[defunct " + name + "]";
//defunct = true;
//energy = 0;
for (Ref r : refs)
r.unindex();
refs.clear();
scenario.change();
}
BaseXRef export() {
ret new BaseXRef(scenario.programID, id);
}
// notice system of a change in this object
void change() {
scenario.change();
}
} // class Concept
// for inter-process communication
// prepared for string ids if we do them later
sclass PassRef {
S id;
*() {} // make serialisation happy
*(long id) { this.id = str(id); }
*(Concept c) { this(c.id); }
long longID() { ret parseLong(id); }
public S toString() {
ret id;
}
}
sclass Event extends Concept {}
// Reference to a concept in another program
sclass BaseXRef {
S programID;
long id;
*() {}
*(S *programID, long *id) {}
public bool equals(O o) {
if (!(o instanceof BaseXRef)) false;
BaseXRef r = cast o;
ret eq(programID, r.programID) && eq(id, r.id);
}
public int hashCode() {
ret programID.hashCode() + (int) id;
}
}
// BaseXRef as a concept
sclass XRef extends Concept {
BaseXRef ref;
*() {}
*(BaseXRef *ref) { _doneLoading2(); }
// after we have been added to scenario
void _doneLoading2() {
getIndex().put(ref, this);
}
HashMap<BaseXRef, XRef> getIndex() {
ret getXRefIndex(scenario);
}
}
static synchronized HashMap<BaseXRef, XRef> getXRefIndex(Scenario scenario) {
HashMap cache = (HashMap) scenario.perClassData.get(XRef.class);
if (cache == null)
scenario.perClassData.put(XRef.class, cache = new HashMap);
ret cache;
}
// uses mainScenario
static XRef lookupOrCreateXRef(BaseXRef ref) {
XRef xref = getXRefIndex(mainScenario).get(ref);
if (xref == null)
xref = new XRef(ref);
ret xref;
}
svoid loadAndAutoSaveConcepts {
mainScenario.loadConcepts();
mainScenario.autoSaveConcepts();
}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: | 998 / 1164 |
| Version history: | 1 change(s) |
| Referenced in: | #1005278 - Test Scenarios |