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

514
LINES

< > BotCompany Repo | #1005277 // Scenarios (old, now called "concepts")

JavaX fragment (include)

// 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();
}

Author comment

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: 481 / 683
Version history: 1 change(s)
Referenced in: [show references]