import java.util.*;
import java.util.zip.*;
import java.util.List;
import java.util.regex.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.table.*;
import java.io.*;
import java.net.*;
import java.lang.reflect.*;
import java.lang.ref.*;
import java.lang.management.*;
import java.security.*;
import java.security.spec.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.imageio.*;
import java.math.*;
import static x30_pkg.x30_util.DynamicObject;
import java.nio.file.Path;
import java.text.*;
import java.text.NumberFormat;
import java.nio.charset.Charset;
import java.util.TimeZone;
import java.text.SimpleDateFormat;
class main {
static class ConceptFieldIndex extends ConceptFieldIndexBase {
ConceptFieldIndex(Class cc, String field) { super(cc, field); }
ConceptFieldIndex(Concepts concepts, Class cc, String field) { super(concepts, cc, field); }
void init() {
valueToObject = new MultiSetMap();
}
void register() {
concepts.addFieldIndex(cc, field, this);
}
}
// A concept should be an object, not just a string.
// Functions that should always be there for child processes:
static int concepts_internStringsLongerThan = 10;
static ThreadLocal concepts_unlisted = new ThreadLocal();
// BREAKING CHANGE 2021/6/7 set to true
static boolean concepts_unlistedByDefault = true; // true = we can create instances of concepts with "new" without registering them automatically
interface IConceptIndex {
void update(Concept c); // also for adding
void remove(Concept c);
}
interface IFieldIndex {
Collection getAll(Val val);
List allValues(); // returns a cloned list
MultiSet allValues_multiSet();
IterableIterator objectIterator();
}
// Approach to persisting the Concepts object itself (in normal
// DB operation, this is not done): For simplification, speed and
// compactness, we make almost all the fields transient and store only // the concepts and the idCounter. To unstructure the Concepts object,
// use unstructureConcepts() or postUnstructureConcepts(), then
// re-set up any indices, listeners etc.
// base class indicating nothing
static class ConceptsChange {}
// change of a single concept
static class ConceptCreate extends ConceptsChange implements IFieldsToList{
Concept c;
ConceptCreate() {}
ConceptCreate(Concept c) {
this.c = c;}
public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + c + ")"; }
public boolean equals(Object o) {
if (!(o instanceof ConceptCreate)) return false;
ConceptCreate __4 = (ConceptCreate) o;
return eq(c, __4.c);
}
public int hashCode() {
int h = -1751266972;
h = boostHashCombine(h, _hashCode(c));
return h;
}
public Object[] _fieldsToList() { return new Object[] {c}; }
}
// change of a single concept
static class ConceptChange extends ConceptsChange implements IFieldsToList{
Concept c;
ConceptChange() {}
ConceptChange(Concept c) {
this.c = c;}
public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + c + ")"; }
public boolean equals(Object o) {
if (!(o instanceof ConceptChange)) return false;
ConceptChange __5 = (ConceptChange) o;
return eq(c, __5.c);
}
public int hashCode() {
int h = -1760609256;
h = boostHashCombine(h, _hashCode(c));
return h;
}
public Object[] _fieldsToList() { return new Object[] {c}; }
}
// removal of a single concept
// c.id is going to become 0 at some point, so we pass the
// id separately
static class ConceptDelete extends ConceptsChange implements IFieldsToList{
static final String _fieldOrder = "id c";
long id;
Concept c;
ConceptDelete() {}
ConceptDelete(long id, Concept c) {
this.c = c;
this.id = id;}
public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + id + ", " + c + ")"; }
public boolean equals(Object o) {
if (!(o instanceof ConceptDelete)) return false;
ConceptDelete __6 = (ConceptDelete) o;
return id == __6.id && eq(c, __6.c);
}
public int hashCode() {
int h = -1734431213;
h = boostHashCombine(h, _hashCode(id));
h = boostHashCombine(h, _hashCode(c));
return h;
}
public Object[] _fieldsToList() { return new Object[] {id, c}; }
}
// unknown change anywhere in concepts; consider it all dirty
// (this one should not be used except for batch jobs)
static class FullChange extends ConceptsChange implements IFieldsToList{
FullChange() {}
public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + ")"; }
public boolean equals(Object o) {
return o instanceof FullChange;
}
public int hashCode() {
int h = 733452095;
return h;
}
public Object[] _fieldsToList() { return null; }
}
static class Concepts implements AutoCloseable {
Map concepts = synchroTreeMap();
long idCounter;
transient HashMap perClassData;
transient Map miscMap; // don't use directly, call miscMap... methods to access
// set to "-" for non-persistent (possibly not implemented)
// also, can include a case ID ("#123/1")
// TODO: have an actual directory instead
transient String programID;
transient Concepts parent; // new mechanism
transient volatile long changes, changesWritten, lastChange;
transient volatile java.util.Timer autoSaver;
transient volatile boolean dontSave = false;
transient volatile boolean savingConcepts, noXFullGrab;
transient boolean vmBusSend = true;
transient boolean initialSave = false; // set to false to avoid initial useless saving
transient int autoSaveInterval = -1000; // 1 second + wait logic
transient boolean useGZIP = true, quietSave;
transient ReentrantLock lock = new ReentrantLock(true);
transient ReentrantLock saverLock = new ReentrantLock(true);
transient long lastSaveTook = -1, lastSaveWas, loadTook, uncompressedSize;
transient float maxAutoSavePercentage = 10;
transient List conceptIndices;
transient Map, Map> fieldIndices;
transient Map, Map> ciFieldIndices;
//transient L saveActions = synchroList();
transient List preSave;
transient Object classFinder = _defaultClassFinder();
transient List onAllChanged = synchroList(); // list of runnables
transient Set onChange = new HashSet();
transient Object saveWrapper; // VF1, to profile saving
transient boolean modifyOnCreate = false; // set _modified == created initially
transient boolean modifyOnBackRef = false; // set modified if back refs change
transient boolean useFileLock = true; // instead of locking by bot
// OLD - not done anymore. transient bool collectForwardRefs
transient FileBasedLock fileLock;
transient boolean storeBaseClassesInStructure = false; // helps with schema evolution when concept subclasses disappear
transient boolean useBackRefsForSearches = false; // assume backRefs are sane in order to speed up searches
transient boolean defunct = false;
transient int newBackupEveryXMinutes = 60;
// add more fields for Concepts here
Concepts() {}
Concepts(String programID) {
this.programID = programID;}
synchronized long internalID() {
do {
++idCounter;
} while (hasConcept(idCounter));
return idCounter;
}
synchronized HashMap perClassData() {
if (perClassData == null) perClassData = new HashMap();
return perClassData;
}
void initProgramID() {
if (programID == null)
programID = getDBProgramID();
}
// Now tries to load from bot first, then go to disk.
Concepts load() {
return load(false);
}
Concepts safeLoad() {
return load(true);
}
Concepts load(boolean allDynamic) {
initProgramID();
// try custom grabber
Object dbGrabber = miscMapGet("dbGrabber");
if (dbGrabber != null && !isFalse(callF(dbGrabber)))
return this;
try {
if (tryToGrab(allDynamic)) return this;
} catch (Throwable e) {
if (!exceptionMessageContains(e, "no xfullgrab"))
printShortException(e);
print("xfullgrab failed - loading DB of " + programID + " from disk");
}
return loadFromDisk(allDynamic);
}
Concepts loadFromDisk() { return loadFromDisk(false); }
Concepts loadFromDisk(boolean allDynamic) {
if (nempty(concepts)) clearConcepts();
//DynamicObject_loading.set(true); // now done in unstructure()
//try {
// minimal crash recovery
restoreLatestBackupIfConceptsFileEmpty(programID, "doIt" , true);
long time = now();
Map _concepts = concepts; // empty map
readLocally2_allDynamic.set(allDynamic);
AutoCloseable __3 = tempSetTL(readLocally2_classFinder, classFinder); try {
readLocally2(this, programID, "concepts");
Map __concepts = concepts;
concepts = _concepts;
concepts.putAll(__concepts);
int l = readLocally_stringLength;
int tokrefs = unstructure_tokrefs;
assignConceptsToUs();
loadTook = now()-time;
done("Loaded " + n(l(concepts), "concepts"), time);
try {
if (fileSize(getProgramFile(programID, "idCounter.structure")) != 0)
readLocally2(this, programID, "idCounter");
else
calcIdCounter();
} catch (Throwable e) { printStackTrace(e);
calcIdCounter();
}
/*} finally {
DynamicObject_loading.set(null);
}*/
return this;
} finally { _close(__3); }}
Concepts loadConcepts() { return load(); }
boolean tryToGrab(boolean allDynamic) {
if (sameSnippetID(programID, getDBProgramID())) return false;
RemoteDB db = connectToDBOpt(programID); try {
if (db != null) {
loadGrab(db.fullgrab(), allDynamic);
return true;
}
return false;
} finally { _close(db); }}
Concepts load(String grab) {
return loadGrab(grab, false);
}
Concepts safeLoad(String grab) {
return loadGrab(grab, true);
}
Concepts loadGrab(String grab, boolean allDynamic) {
clearConcepts();
DynamicObject_loading.set(true);
try {
Map map = (Map) unstructure(grab, allDynamic, classFinder);
concepts.putAll(map);
assignConceptsToUs();
for (long l : map.keySet())
idCounter = max(idCounter, l);
} finally {
DynamicObject_loading.set(null);
}
//XXX allChanged(); // Nobody is listening at this point anyway
return this;
}
void assignConceptsToUs() {
// fix unstructure bugs
for (Pair p: mapToPairs((Map) (Map) concepts))
if (!(p.b instanceof Concept)) {
print("DROPPING non-existant concept " + p.a + ": " + dynShortName(p.b));
concepts.remove(p.a);
}
for (Concept c : values(concepts)) c._concepts = this;
for (Concept c : values(concepts))
c._doneLoading2(); // doneLoading2 is called on all concepts after all concepts are loaded
}
String progID() {
return programID == null ? getDBProgramID() : programID;
}
Concept getConcept(String id) {
return empty(id) ? null : getConcept(parseLong(id));
}
Concept getConcept(long id) {
return (Concept) concepts.get((long) id);
}
Concept getConcept(RC ref) {
return ref == null ? null : getConcept(ref.longID());
}
boolean hasConcept(long id) {
return concepts.containsKey((long) id);
}
void deleteConcept(long id) {
Concept c = getConcept(id);
if (c == null)
print("Concept " + id + " not found");
else
c.delete();
}
void calcIdCounter() {
long id_ = 0;
for (long id : keys(concepts))
id_ = max(id_, id);
idCounter = id_+1;
saveLocally2(this, programID, "idCounter");
}
File conceptsFile() {
return getProgramFile(programID, useGZIP ? "concepts.structure.gz" : "concepts.structure");
}
// used for locking when useFileLock is activated
File lockFile() {
return getProgramFile(programID, "concepts.lock");
}
FileBasedLock fileLock() {
if (fileLock == null)
fileLock = new FileBasedLock(lockFile());
return fileLock;
}
void saveConceptsIfDirty() { saveConcepts(); }
void save() { saveConcepts(); }
void saveConcepts() {
vmBus_send("saveConceptsCalled", Concepts.this);
if (dontSave) return;
initProgramID();
saverLock.lock();
savingConcepts = true;
long start = now(), time;
try {
String s = null;
//synchronized(main.class) {
long _changes = changes;
if (_changes == changesWritten) return;
File f = conceptsFile();
lock.lock();
long fullTime = now();
try {
saveLocally2(this, programID, "idCounter");
vmBus_send("idCounterWritten", Concepts.this, programID, "idCounter");
if (useGZIP) {
vmBus_send("callingSaveWrapper", Concepts.this, saveWrapper);
callRunnableWithWrapper(saveWrapper, new Runnable() { public void run() { try {
vmBus_send("callingPreSave", Concepts.this, preSave);
callFAll(preSave);
vmBus_send("writingFile", Concepts.this, f);
uncompressedSize = saveGZStructureToFile(f, cloneMap(concepts), makeStructureData());
vmBus_send("gzFileSaved", Concepts.this, f, uncompressedSize);
} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "vmBus_send callingPreSave(Concepts.this, preSave);\r\n callFAll(preS..."; }});
getProgramFile(programID, "concepts.structure").delete();
} else
s = fullStructure();
} finally {
lock.unlock();
}
/*while (nempty(saveActions))
pcallF(popFirst(saveActions));*/
changesWritten = _changes; // only update when structure didn't fail
if (!useGZIP) {
time = now()-start;
if (!quietSave)
print("Saving " + toM(l(s)) + "M chars (" /*+ changesWritten + ", "*/ + time + " ms)");
start = now();
saveTextFile(f, javaTokWordWrap(s));
getProgramFile(programID, "concepts.structure.gz").delete();
}
File backupFile = getProgramFile(programID, "backups/concepts.structure" + (useGZIP ? ".gz" : "") + ".backup" + ymd() + "-" + formatInt(hours(), 2)
+ (newBackupEveryXMinutes >= 60 ? "" : formatInt(roundDownTo_rev(minutes(), newBackupEveryXMinutes), 2)));
copyFile(f, backupFile);
time = now()-start;
if (!quietSave)
print(programID + ": Saved " + toK(f.length()) + " K, " + n(concepts, "concepts") + " (" + time + " ms)");
lastSaveWas = fullTime;
lastSaveTook = now()-fullTime;
} finally {
savingConcepts = false;
saverLock.unlock();
}
}
void _autoSaveConcepts() {
if (autoSaveInterval < 0 && maxAutoSavePercentage != 0) {
long pivotTime = Math.round(lastSaveWas+lastSaveTook*100.0/maxAutoSavePercentage);
if (now() < pivotTime) {
//print("Skipping auto-save (last save took " + lastSaveTook + ")");
return;
}
}
try {
saveConcepts();
} catch (Throwable e) {
print("Concept save failed, will try again");
printStackTrace(e);
}
}
String fullStructure() {
return structure(cloneMap(concepts), makeStructureData());
}
transient IF0 makeStructureData;
structure_Data makeStructureData() { return makeStructureData != null ? makeStructureData.get() : makeStructureData_base(); }
final structure_Data makeStructureData_fallback(IF0 _f) { return _f != null ? _f.get() : makeStructureData_base(); }
structure_Data makeStructureData_base() {
return finishStructureData(new structure_Data());
}
structure_Data finishStructureData(structure_Data data) {
if (storeBaseClassesInStructure)
data.storeBaseClasses = true;
return data;
}
void clearConcepts() {
for (Concept c : allConcepts()) c.delete();
//concepts.clear();
//allChanged();
}
void fireLegacyChangeEvent() {
synchronized(this) { ++changes; lastChange = sysNow(); }
if (vmBusSend) vmBus_send("conceptsChanged", this);
pcallFAll(onAllChanged);
}
// auto-save every second if dirty
synchronized void autoSaveConcepts() {
if (autoSaver == null) {
if (isTransient()) throw fail("Can't persist transient database");
autoSaver = doEvery_daemon("Concepts Saver for " + programID,
abs(autoSaveInterval), new Runnable() { public void run() { try { _autoSaveConcepts() ;
} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "_autoSaveConcepts()"; }});
// print("Installed auto-saver (" + autoSaveInterval + " ms, " + progID() + ")");
}
}
public void close() { cleanMeUp(); }
void cleanMeUp() {
try {
defunct = true;
boolean shouldSave = autoSaver != null;
if (autoSaver != null) {
autoSaver.cancel();
autoSaver = null;
}
while (savingConcepts) sleepInCleanUp(10);
if (shouldSave)
saveConceptsIfDirty();
} catch (Throwable __e) { printStackTrace(__e); }
{ cleanUp(fileLock); fileLock = null; }
}
Map getIDsAndNames() {
Map map = new HashMap();
Map cloned = cloneMap(concepts);
for (long id : keys(cloned))
map.put(id, cloned.get(id).className);
return map;
}
void deleteConcepts(List l) {
ping();
if (l != null) for (Object o : cloneList(l))
if (o instanceof Long) {
Concept c = concepts.get(o);
if (c != null) c.delete();
} else if (o instanceof Concept)
((Concept) o).delete();
else
warn("Can't delete " + getClassName(o));
}
A conceptOfType(Class type) {
IConceptCounter counter = conceptCounterForClass(type);
if (counter != null) return (A) first(counter.allConcepts());
return firstOfType(allConcepts(), type);
}
List conceptsOfType(Class type) {
List l = conceptsOfType_noParent(type);
if (parent == null) return l;
return concatLists_conservative(l, parent.conceptsOfType(type));
}
List conceptsOfType_noParent(Class type) {
ping();
IConceptCounter counter = conceptCounterForClass(type);
if (counter != null) return (List) cloneList(counter.allConcepts());
return filterByType(allConcepts(), type);
}
List listConcepts(Class type) {
return conceptsOfType(type);
}
List list(Class type) {
return conceptsOfType(type);
}
List list_noParent(Class type) {
return conceptsOfType_noParent(type);
}
// TODO: would be better to make this Cl (indices may return sets)
List list(String type) {
return conceptsOfType(type);
}
List conceptsOfType(String type) {
return filterByDynamicType(allConcepts(), "main$" + type);
}
boolean hasConceptOfType(Class extends Concept> type) {
return hasType(allConcepts(), type);
}
void persistConcepts() {
loadConcepts();
autoSaveConcepts();
}
// We love synonyms
void conceptPersistence() { persistConcepts(); }
Concepts persist() { persistConcepts(); return this; }
void persist(Integer interval) {
if (interval != null) autoSaveInterval = interval;
persist();
}
// Runs r if there is no concept of that type
A ensureHas(Class c, Runnable r) {
A a = conceptOfType(c);
if (a == null) {
r.run();
a = conceptOfType(c);
if (a == null)
throw fail("Concept not made by " + r + ": " + shortClassName(c));
}
return 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, Object func) {
for (Concept a : conceptsOfType(c1)) {
Concept b = findBackRef(a, c2);
if (b == null) {
callF(func, a);
b = findBackRef(a, c2);
if (b == null)
throw fail("Concept not made by " + func + ": " + shortClassName(c2));
}
}
}
// Type of func: voidfunc(concept)
void forEvery(Class extends Concept> type, Object func) {
for (Concept c : conceptsOfType(type))
callF(func, c);
}
int deleteAll(Class extends Concept> type) {
List l = (List) conceptsOfType(type);
for (Concept c : l) c.delete();
return l(l);
}
// always returns a new list (callers depend on this)
Collection allConcepts() {
synchronized(concepts) {
return new ArrayList(values(concepts));
}
}
IConceptCounter conceptCounterForClass(Class extends Concept> c) {
for (IFieldIndex idx : values(mapGet(fieldIndices, c)))
if (idx instanceof IConceptCounter) return ((IConceptCounter) idx);
for (IFieldIndex idx : values(mapGet(ciFieldIndices, c)))
if (idx instanceof IConceptCounter) return ((IConceptCounter) idx);
return null;
}
int countConcepts(Class c, Object... params) {
int n = countConcepts_noParent(c, params);
if (parent == null) return n;
return n+parent.countConcepts(c, params);
}
int countConcepts_noParent(Class c, Object... params) {
ping();
if (empty(params)) {
IConceptCounter counter = conceptCounterForClass(c);
if (counter != null) return counter.countConcepts();
return l(list_noParent(c));
}
int n = 0;
for (A x : list_noParent(c)) if (checkConceptFields(x, params)) ++n;
return n;
}
int countConcepts(String c, Object... params) {
ping();
if (empty(params)) return l(list(c));
int n = 0;
for (Concept x : list(c)) if (checkConceptFields(x, params)) ++n;
return n;
}
int countConcepts() {
return l(concepts);
}
synchronized List clonedConceptIndices() {
return cloneList(conceptIndices);
}
synchronized void addConceptIndex(IConceptIndex index) {
if (conceptIndices == null)
conceptIndices = new ArrayList();
conceptIndices.add(index);
}
synchronized void removeConceptIndex(IConceptIndex index) {
if (conceptIndices == null) return;
conceptIndices.remove(index);
if (empty(conceptIndices)) conceptIndices = null;
}
synchronized void addFieldIndex(Class extends Concept> c, String field, IFieldIndex index) {
if (fieldIndices == null)
fieldIndices = new HashMap();
Map map = fieldIndices.get(c);
if (map == null)
fieldIndices.put(c, map = new HashMap());
map.put(field, index);
}
synchronized void removeFieldIndex(Class extends Concept> c, String field, IFieldIndex index) {
Map map = mapGet(fieldIndices, c);
mapRemove(map, field);
}
synchronized IFieldIndex getFieldIndex(Class extends Concept> c, String field) {
if (fieldIndices == null) return null;
Map map = fieldIndices.get(c);
return map == null ? null : map.get(field);
}
synchronized IFieldIndex getAnyIndexForClass(Class extends Concept> c) {
return firstValue(fieldIndices == null ? null : fieldIndices.get(c));
}
synchronized void addCIFieldIndex(Class extends Concept> c, String field, IFieldIndex index) {
if (ciFieldIndices == null)
ciFieldIndices = new HashMap();
Map map = ciFieldIndices.get(c);
if (map == null)
ciFieldIndices.put(c, map = new HashMap());
map.put(field, index);
}
synchronized void removeCIFieldIndex(Class extends Concept> c, String field) {
Map map = mapGet(ciFieldIndices, c);
mapRemove(map, field);
}
synchronized IFieldIndex getCIFieldIndex(Class extends Concept> c, String field) {
if (ciFieldIndices == null) return null;
Map map = ciFieldIndices.get(c);
return map == null ? null : map.get(field);
}
// inter-process methods
RC xnew(String name, Object... values) {
return new RC(cnew(name, values));
}
void xset(long id, String field, Object value) {
xset(new RC(id), field, value);
}
void xset(RC c, String field, Object value) {
if (value instanceof RC)
value = getConcept((RC) value);
cset(getConcept(c), field, value);
}
Object xget(long id, String field) {
return xget(new RC(id), field);
}
Object xget(RC c, String field) {
return xgetPost(cget(getConcept(c), field));
}
Object xgetPost(Object o) {
o = deref(o);
if (o instanceof Concept)
return new RC((Concept) o);
return o;
}
void xdelete(long id) {
xdelete(new RC(id));
}
void xdelete(RC c) {
getConcept(c).delete();
}
void xdelete(List l) {
for (RC c : l)
xdelete(c);
}
List xlist() {
return map("toPassRef", allConcepts());
}
List xlist(String className) {
return map("toPassRef", conceptsOfType(className));
}
boolean isTransient() { return eq(programID, "-"); }
String xfullgrab() {
if (noXFullGrab) throw fail("no xfullgrab (DB too large)");
Lock __1 = lock(); lock(__1); try {
if (changes == changesWritten && !isTransient())
return loadConceptsStructure(programID);
return fullStructure();
} finally { unlock(__1); } }
/* dev.
Either xfullgrabGZipped() {
lock lock();
if (changes == changesWritten && !isTransient())
ret loadConceptsStructure(programID);
ret fullStructure();
}*/
void xshutdown() {
// Killing whole VM if someone wants this DB to shut down
cleanKillVM();
}
long xchangeCount() { return changes; }
int xcount() { return countConcepts(); }
void register(Concept c) {
ping();
if (c._concepts == this) return;
if (c._concepts != null) throw fail("Can't re-register");
c.id = internalID();
c.created = now();
if (modifyOnCreate) c._setModified(c.created);
register_phase2(c);
vmBus_send("conceptCreated", c);
fireChange(new ConceptCreate(c));
}
// also called by replaceConceptAndUpdateRefs
void register_phase2(Concept c) {
c._concepts = this;
concepts.put((long) c.id, c);
for (Concept.Ref r : c._refs())
r.index();
c.change();
c._onRegistered();
}
void registerKeepingID(Concept c) {
if (c._concepts == this) return;
if (c._concepts != null) throw fail("Can't re-register");
c._concepts = this;
concepts.put((long) c.id, c);
c.change();
}
void conceptChanged(Concept c) {
fireChange(new ConceptChange(c));
if (conceptIndices != null)
for (IConceptIndex index : clonedConceptIndices())
index.update(c);
}
boolean hasUnsavedData() {
return changes != changesWritten || savingConcepts;
}
synchronized Object miscMapGet(Object key) {
return mapGet(miscMap, key);
}
synchronized Object miscMapPut(Object key, Object value) {
if (miscMap == null) miscMap = new HashMap();
return miscMap.put(key, value);
}
synchronized void miscMapRemove(Object key) {
mapRemove(miscMap, key);
}
// Note: auto-typing can fool you, make sure create returns
// a wide enough type
synchronized A miscMapGetOrCreate(Object key, IF0 create) {
if (containsKey(miscMap, key)) return (A) miscMap.get(key);
A value = create.get();
miscMapPut(key, value);
return value;
}
void setParent(Concepts parent) {
this.parent = parent;
}
void fireChange(ConceptsChange change) {
if (change == null) return;
pcallFAll(onChange, change);
fireLegacyChangeEvent();
}
final void onChange(IVF1 l){ addChangeListener(l); }
void addChangeListener(IVF1 l) {
syncAdd(onChange, l);
}
void removeChangeListener(IVF1 l) {
syncRemove(onChange, l);
}
void addPreSave(Runnable r) {
preSave = syncAddOrCreate(preSave, r);
}
public String toString() {
return nConcepts(concepts) + " (" + programID + ", hash: " + identityHashCode(this) + ")";
}
} // end of Concepts
static class Concept extends DynamicObject {
transient Concepts _concepts; // Where we belong
long id;
long created, _modified;
List[ refs;
List][ backRefs;
// used only internally (cnew)
Concept(String className) {
super(className);
_created();
}
Concept() {
if (!_loading()) {
//className = shortClassName(this); // XXX - necessary?
//print("New concept of type " + className);
_created();
}
}
Concept(boolean unlisted) {
if (!unlisted) _created();
}
public String toString() {
return shortDynamicClassName(this) + " " + id;
}
static boolean loading() { return _loading(); }
static boolean _loading() { return dynamicObjectIsLoading(); }
void _created() {
if (!concepts_unlistedByDefault && !eq(concepts_unlisted.get(), true))
db_mainConcepts().register(this);
}
// base class + required interface. experimental
class TypedRef extends Ref {
TypedRef() {}
//Class aType;
Class bType;
TypedRef(Class bType) {
this.bType = bType;}
TypedRef(Class bType, B value) {
this.bType = bType; set((A) value); }
TypedRef(B value) { set((A) value); }
public boolean set(A a) {
return super.set(checkValue(a));
}
void check() { checkValue(get()); }
C checkValue(C a) {
if (bType != null && a != null)
assertIsInstance(a, bType);
return a;
}
B b() { return (B) value; }
}
class Ref implements IRef {
A value;
Ref() {
if (!dynamicObjectIsLoading())
registerRef();
}
void registerRef() {
vmBus_send("registeringConceptRef", this);
}
Ref(A value) {
this.value = value;
registerRef();
index();
}
// get owning concept (source)
Concept concept() {
return Concept.this;
}
// get target
public A get() { return value; }
public boolean has() { return value != null; }
boolean set(A a) {
if (a == value) return false;
unindex();
value = a;
index();
change();
return true;
}
void setIfEmpty(A a) {
if (!has()) set(a);
}
public void set(Ref ref) { set(ref.get()); }
public void clear() { set((A) null); }
boolean validRef() {
return value != null && _concepts != null && _concepts == value._concepts;
}
// TODO: sync all the indexing and unindexing!?
void index() {
if (validRef()) {
value._addBackRef(this);
change();
}
}
Ref unindex() {
if (validRef()) {
value._removeBackRef(this);
change();
}
return this;
}
void unindexAndDrop() {
unindex();
_removeRef(this);
}
void change() {
Concept.this.change();
}
public String toString() { return
str(value); }
}
class RefL extends AbstractList {
List][> l = new ArrayList();
RefL() {}
RefL(List l) { replaceWithList(l); }
public void clear() {
while (!isEmpty()) removeLast(this);
}
public void replaceWithList(List l) {
clear();
for (A a : unnullForIteration(l)) add(a);
}
public A set(int i, A o) {
Ref ref = syncGet(l, i);
A prev = ref.get();
ref.set(o);
return prev;
}
public void add(int i, A o) {
syncAdd(l, i, new Ref(o));
}
public A get(int i) {
return syncGet(l, i).get();
}
public A remove(int i) {
return syncRemove(l, i).get();
}
public int size() {
return syncL(l);
}
public boolean contains(Object o) {
if (o instanceof Concept)
for (Ref r : l) if (eq(r.get(), o)) return true;
return super.contains(o);
}
}
void delete() {
//name = "[defunct " + name + "]";
//defunct = true;
//energy = 0;
// clean refs
for (Ref r : unnullForIteration(_refs()))
r.unindex();
refs = null;
// set back refs to null
for (Ref r : cloneList(backRefs))
r.set((Concept) null);
backRefs = null;
var _concepts = this._concepts;
if (_concepts != null) {
_concepts.concepts.remove(id);
_concepts.fireChange(new ConceptDelete(id, this));
if (_concepts.conceptIndices != null)
for (IConceptIndex index : _concepts.conceptIndices)
index.remove(this);
this._concepts = null;
}
id = 0;
}
BaseXRef export() {
return new BaseXRef(_concepts.progID(), id);
}
// notice system of a change in this object
void change() {
_setModified(now());
_change_withoutUpdatingModifiedField();
}
void _setModified(long modified) {
_modified = modified;
}
void _change_withoutUpdatingModifiedField() {
if (_concepts != null) _concepts.conceptChanged(this);
}
void _change() { change(); }
String _programID() {
return _concepts == null ? getDBProgramID() : _concepts.progID();
}
// overridable
void _addBackRef(Concept.Ref ref) {
backRefs = addDyn_quickSync(backRefs, ref);
_backRefsModified();
}
void _backRefsModified() {
if (_concepts != null && _concepts.modifyOnBackRef) change();
}
void _removeBackRef(Concept.Ref ref) {
backRefs = removeDyn_quickSync(backRefs, ref);
_backRefsModified();
}
void _removeRef(Concept.Ref ref) {
refs = removeDyn_quickSync(refs, ref);
}
int _backRefCount() { return syncL(backRefs); }
// convenience methods
final void setField(String field, Object value){ _setField(field, value); }
void _setField(String field, Object value) {
cset(this, field, value);
}
boolean setField_trueIfChanged(String field, Object value) {
return cset(this, field, value) != 0;
}
A setFieldAndReturn(String field, A value) {
setField(field, value);
return value;
}
final void setFields(Object... values){ _setFields(values); }
void _setFields(Object... values) {
cset(this, values);
}
Concepts concepts() { return _concepts; }
boolean isDeleted() { return id == 0; }
void _doneLoading2() {
Map map = _fieldMigrations();
if (map != null) for (Map.Entry extends String, ? extends FieldMigration> __0 : _entrySet( map))
{ String oldField = __0.getKey(); FieldMigration m = __0.getValue(); crenameField_noOverwrite(this, oldField, m.newField); }
}
static class FieldMigration implements IFieldsToList{
String newField;
FieldMigration() {}
FieldMigration(String newField) {
this.newField = newField;}
public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + newField + ")"; }
public boolean equals(Object o) {
if (!(o instanceof FieldMigration)) return false;
FieldMigration __7 = (FieldMigration) o;
return eq(newField, __7.newField);
}
public int hashCode() {
int h = 558692372;
h = boostHashCombine(h, _hashCode(newField));
return h;
}
public Object[] _fieldsToList() { return new Object[] {newField}; }
}
// value is
Map _fieldMigrations() { return null; }
// new wrapper to get a copy of the refs list
// so we can eventually drop the refs field
Collection][ _refs() {
return scanConceptForRefs(this);
}
// for debugging
List][ _rawRefsField() { return refs; }
Concepts _concepts() { return _concepts; }
boolean _conceptsDefunct() { return _concepts != null && _concepts.defunct; }
boolean _conceptsDefunctOrUnregistered() { return _concepts == null || _concepts.defunct; }
// allow refs to do magic stuff?
void _onRegistered() {
/*for (Ref ref : _refs())
refs._onRegistered();*/
}
boolean addAndChange(Collection cl, A a) {
if (cl == null || !cl.add(a)) return false;
change();
return true;
}
void clearAndChange(Collection cl) {
if (cl == null) return;
cl.clear();
change();
}
} // end of Concept
// remote reference (for inter-process communication or
// external databases). Formerly "PassRef".
// prepared for string ids if we do them later
static class RC {
transient Object owner;
String id;
RC() {} // make serialisation happy
RC(long id) { this.id = str(id); }
RC(Object owner, long id) { this.id = str(id); this.owner = owner; }
RC(Concept c) { this(c.id); }
long longID() { return parseLong(id); }
public String toString() {
return id;
}
transient RemoteDB db;
String getString(String field) { return db.xS(this, field); }
Object get(String field) { return db.xget(this, field); }
void set(String field, Object value) { db.xset(this, field, value); }
}
// Reference to a concept in another program
static class BaseXRef {
String programID;
long id;
BaseXRef() {}
BaseXRef(String programID, long id) {
this.id = id;
this.programID = programID;}
public boolean equals(Object o) {
if (!(o instanceof BaseXRef)) return false;
BaseXRef r = (BaseXRef) o;
return eq(programID, r.programID) && eq(id, r.id);
}
public int hashCode() {
return programID.hashCode() + (int) id;
}
}
// BaseXRef as a concept
static class XRef extends Concept {
BaseXRef ref;
XRef() {}
XRef(BaseXRef ref) {
this.ref = ref; _doneLoading2(); }
// after we have been added to concepts
void _doneLoading2() {
getIndex().put(ref, this);
}
HashMap getIndex() {
return getXRefIndex(_concepts);
}
}
static synchronized HashMap getXRefIndex(Concepts concepts) {
HashMap cache = (HashMap) concepts.perClassData().get(XRef.class);
if (cache == null)
concepts.perClassData.put(XRef.class, cache = new HashMap());
return cache;
}
// uses mainConcepts
static XRef lookupOrCreateXRef(BaseXRef ref) {
XRef xref = getXRefIndex(db_mainConcepts()).get(ref);
if (xref == null)
xref = new XRef(ref);
return xref;
}
// define standard concept functions to use main concepts
// Now in db_mainConcepts()
/*static void cleanMeUp_concepts() {
if (db_mainConcepts() != null) db_mainConcepts().cleanMeUp();
// mainConcepts = null; // TODO
}*/
static void loadAndAutoSaveConcepts() {
db_mainConcepts().persist();
}
static void loadAndAutoSaveConcepts(int interval) {
db_mainConcepts().persist(interval);
}
static RC toPassRef(Concept c) {
return new RC(c);
}
// so we can instantiate the program to run as a bare DB bot
static void concepts_setUnlistedByDefault(boolean b) {
concepts_unlistedByDefault = b;
}
abstract static class ConceptFieldIndexBase implements IConceptIndex, IFieldIndex, IConceptCounter, AutoCloseable {
Concepts concepts;
Class cc;
String field;
Map objectToValue = syncHashMap();
MultiSetMap valueToObject; // initialized in subclass
// bool indexNulls = true; // set this only when there are few null values in this field in the db. or when the point is to index a non-existing field (for singletons)
ConceptFieldIndexBase() { init(); }
ConceptFieldIndexBase(Class cc, String field) { this(db_mainConcepts(), cc, field); }
ConceptFieldIndexBase(Concepts concepts, Class cc, String field) {
this();
this.field = field;
this.cc = cc;
this.concepts = concepts;
concepts.addConceptIndex(this);
updateAll();
register();
updateAll();
}
void updateAll() {
for (A c : setToIndex())
updateImpl(c);
}
Collection setToIndex() {
return list(concepts, cc);
}
abstract void init();
abstract void register();
public void update(Concept c) {
if (!isInstance(cc, c)) return;
updateImpl((A) c);
}
synchronized void updateImpl(A c) {
Val newValue = (Val) (cget(c, field));
Val oldValue = objectToValue.get(c);
if (newValue == oldValue && (oldValue != null || objectToValue.containsKey(c))) return;
valueToObject.remove(oldValue, c);
valueToObject.put(newValue, c);
put(objectToValue, c, newValue);
}
public synchronized void remove(Concept c) {
if (!isInstance(cc, c)) return;
if (!objectToValue.containsKey(c)) return;
Val value = objectToValue.get(c);
objectToValue.remove(c);
valueToObject.remove(value, (A) c);
}
synchronized A get(Val value) {
return valueToObject.getFirst(value);
}
// older version - may return empty list as null
// should be phased out everywhere!
public synchronized Collection getAll(Val value) {
return valueToObject.get(value);
}
public synchronized List allValues() { return cloneKeys_noSync(valueToObject.data); }
public IterableIterator objectIterator() {
return navigableMultiSetMapValuesIterator_concurrent(valueToObject, this);
}
public synchronized MultiSet allValues_multiSet() {
return multiSetMapToMultiSet(valueToObject);
}
public Class extends Concept> conceptClass() { return cc; }
public int countConcepts() { return l(objectToValue); }
public Collection allConcepts() { return (Collection) keys(objectToValue); }
Object mutex() { return this; }
public void close() { try {
concepts.removeConceptIndex(this);
} catch (Exception __e) { throw rethrow(__e); } }
}
// uses hash sets as inner sets unless subclassed
// uses a hash map as the outer map by default
static class MultiSetMap {
Map> data = new HashMap>();
int size; // number of values
MultiSetMap() {}
MultiSetMap(boolean useTreeMap) { if (useTreeMap) data = new TreeMap(); }
MultiSetMap(MultiSetMap map) { putAll(map); }
MultiSetMap(Map> data) {
this.data = data;}
boolean put(A key, B value) { synchronized(data) {
Set set = data.get(key);
if (set == null)
data.put(key, set = _makeEmptySet());
if (!set.add(value)) return false;
{ ++size; return true; }
}}
boolean add(A key, B value) { return put(key, value); }
void addAll(A key, Collection values) { synchronized(data) {
putAll(key, values);
}}
void addAllIfNotThere(A key, Collection values) { synchronized(data) {
for (B value : values)
setPut(key, value);
}}
void setPut(A key, B value) { synchronized(data) {
if (!containsPair(key, value))
put(key, value);
}}
final boolean contains(A key, B value){ return containsPair(key, value); }
boolean containsPair(A key, B value) { synchronized(data) {
return get(key).contains(value);
}}
void putAll(A key, Collection values) { synchronized(data) {
for (B value : values)
put(key, value);
}}
void removeAll(A key, Collection values) { synchronized(data) {
for (B value : values)
remove(key, value);
}}
Set get(A key) { synchronized(data) {
Set set = data.get(key);
return set == null ? Collections. emptySet() : set;
}}
List getAndClear(A key) { synchronized(data) {
List l = cloneList(data.get(key));
remove(key);
return l;
}}
// return null if empty
Set getOpt(A key) { synchronized(data) {
return data.get(key);
}}
// returns actual mutable live set
// creates the set if not there
Set getActual(A key) { synchronized(data) {
Set set = data.get(key);
if (set == null)
data.put(key, set = _makeEmptySet());
return set;
}}
// TODO: this looks unnecessary
void clean(A key) { synchronized(data) {
Set list = data.get(key);
if (list != null && list.isEmpty())
data.remove(key);
}}
Set keySet() { synchronized(data) {
return data.keySet();
}}
Set keys() { synchronized(data) {
return data.keySet();
}}
void remove(A key) { synchronized(data) {
size -= l(data.get(key));
data.remove(key);
}}
void remove(A key, B value) { synchronized(data) {
Set set = data.get(key);
if (set != null) {
if (set.remove(value)) {
--size;
if (set.isEmpty())
data.remove(key);
}
}
}}
void clear() { synchronized(data) {
data.clear();
size = 0;
}}
boolean containsKey(A key) { synchronized(data) {
return data.containsKey(key);
}}
B getFirst(A key) { synchronized(data) {
return first(get(key));
}}
void addAll(MultiSetMap map) { putAll(map); }
void putAll(MultiSetMap map) { synchronized(data) {
for (A key : map.keySet())
putAll(key, map.get(key));
}}
void putAll(Map map) { synchronized(data) {
if (map != null) for (Map.Entry e : map.entrySet())
put(e.getKey(), e.getValue());
}}
int keysSize() { synchronized(data) { return l(data); }}
// full size
int size() { synchronized(data) {
return size;
}}
// count values for key
int getSize(A key) { return l(data.get(key)); }
int count(A key) { return getSize(key); }
// expensive operation
Set reverseGet(B b) { synchronized(data) {
Set l = new HashSet();
for (A key : data.keySet())
if (data.get(key).contains(b))
l.add(key);
return l;
}}
// expensive operation
A keyForValue(B b) { synchronized(data) {
for (A key : data.keySet())
if (data.get(key).contains(b))
return key;
return null;
}}
Map> asMap() { synchronized(data) {
return cloneMap(data);
}}
boolean isEmpty() { synchronized(data) { return data.isEmpty(); }}
// override in subclasses
Set _makeEmptySet() {
return new HashSet();
}
Collection> allLists() {
synchronized(data) {
return new HashSet(data.values());
}
}
List allValues() {
return concatLists(values(data));
}
List> allEntries() { synchronized(data) {
List> l = emptyList(size);
for (Map.Entry extends A, ? extends Set> __0 : _entrySet( data))
{ A a = __0.getKey(); Set set = __0.getValue(); for (B b : set)
l.add(pair(a, b)); }
return l;
}}
Object mutex() { return data; }
public String toString() { return "mm" + str(data); }
Pair firstEntry() { synchronized(data) {
if (empty(data)) return null;
Map.Entry> entry = data.entrySet().iterator().next();
return pair(entry.getKey(), first(entry.getValue()));
}}
A firstKey() { synchronized(data) { return main.firstKey(data); }}
A lastKey() { synchronized(data) { return (A) ((NavigableMap) data).lastKey(); }}
A higherKey(Object a) { synchronized(data) { return (A) ((NavigableMap) data).higherKey(a); }}
}
static interface IF0 {
A get();
}
static interface IFieldsToList {
Object[] _fieldsToList();
}
// the lockFile must be a file separate from any data files.
// It is created & deleted by this class, and will always have
// size 0.
static class FileBasedLock implements AutoCloseable {
File lockFile;
double timeout = 60.0; // in seconds. refresh happens twice as often
boolean verbose = false;
boolean haveLock = false;
java.util.Timer touchTimer;
FileBasedLock() {}
FileBasedLock(File lockFile) {
this.lockFile = lockFile;}
FileBasedLock(File lockFile, double timeout) {
this.timeout = timeout;
this.lockFile = lockFile;}
// returns true iff lock was acquired (or kept)
synchronized boolean tryToLock() {
if (haveLock) return true;
if (fileExists(lockFile)) {
double age = fileAgeInSeconds(lockFile);
if (verbose) print("Lock file age: " + lockFile + " = " + iround(age) + " s");
if (age >= timeout) {
print("Deleting old lock file (program crashed?): " + lockFile + " (age: " + iround(age) + " seconds)");
deleteFile(lockFile);
}
}
try {
mkdirsForFile(lockFile);
java.nio.file.Files.createFile(toPath(lockFile));
acquired();
return true;
} catch (Throwable e) {
printExceptionShort("Can't lock", e);
return false;
}
}
private void acquired() {
haveLock = true;
startTouchTimer();
}
void forceLock() { try {
print("Force-locking " + lockFile);
touchFile(lockFile); // make or touch file
acquired();
} catch (Exception __e) { throw rethrow(__e); } }
String lockError() {
return "Couldn't aquire lock file: " + lockFile;
}
void lockOrFail() {
if (!tryToLock())
throw fail(lockError());
}
synchronized void startTouchTimer() {
if (touchTimer != null) return;
double interval = timeout/2;
touchTimer = doEvery(interval, new Runnable() { public void run() { try { doTouch();
} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "doTouch();"; }});
if (verbose) print("Touch timer started for " + lockFile + " (" + interval + "s)");
}
synchronized void doTouch() { try {
if (haveLock) {
if (verbose) print("Touching lock file: " + lockFile);
touchExistingFile(lockFile);
}
} catch (Throwable __e) { printStackTrace(__e); }}
public synchronized void close() { try {
{ cleanUp(touchTimer); touchTimer = null; }
if (haveLock) {
haveLock = false;
if (verbose) print("Deleting lock file: " + lockFile);
deleteFile(lockFile);
}
} catch (Throwable __e) { printStackTrace(__e); }}
synchronized void _simulateCrash() {
{ cleanUp(touchTimer); touchTimer = null; }
}
void deleteOnExit() {
if (haveLock)
lockFile.deleteOnExit();
}
}
// could almost add IVar here - but void set() and bool set() are incompatible in some places
static interface IRef extends IF0 {
// called by the referencee to atomically replace itself
// Passing oldValue to avoid race conditions
public default void replaceValue(A oldValue, A newValue) {}
}
static interface IConceptCounter {
Class extends Concept> conceptClass();
int countConcepts();
Collection allConcepts();
}
static interface IVF1 {
void get(A a);
}
// you still need to implement hasNext() and next()
static abstract class IterableIterator implements Iterator, Iterable {
public Iterator iterator() {
return this;
}
public void remove() {
unsupportedOperation();
}
}
// uses HashMap by default
static class MultiSet implements IMultiSet {
Map map = new HashMap();
int size; // now maintaining a size counter
MultiSet(boolean useTreeMap) {
if (useTreeMap) map = new TreeMap();
}
MultiSet(TreeMap map) {
this.map = map;}
MultiSet() {}
MultiSet(Iterable c) { addAll(c); }
MultiSet(MultiSet ms) { synchronized(ms) {
for (A a : ms.keySet()) add(a, ms.get(a));
}}
// returns new count
public synchronized int add(A key) { return add(key, 1); }
synchronized void addAll(Iterable c) {
if (c != null) for (A a : c) add(a);
}
synchronized void addAll(MultiSet ms) {
for (A a : ms.keySet()) add(a, ms.get(a));
}
synchronized int add(A key, int count) {
if (count <= 0) return 0; // don't calculate return value in this case
size += count;
Integer i = map.get(key);
map.put(key, i != null ? (count += i) : count);
return count;
}
synchronized void put(A key, int count) {
int oldCount = get(key);
if (count == oldCount) return;
size += count-oldCount;
if (count != 0)
map.put(key, count);
else
map.remove(key);
}
public synchronized int get(A key) {
Integer i = map.get(key);
return i != null ? i : 0;
}
synchronized boolean contains(A key) {
return map.containsKey(key);
}
synchronized void remove(A key) {
Integer i = map.get(key);
if (i != null) {
--size;
if (i > 1)
map.put(key, i - 1);
else
map.remove(key);
}
}
synchronized List topTen() { return getTopTen(); }
synchronized List getTopTen() { return getTopTen(10); }
synchronized List getTopTen(int maxSize) {
List list = getSortedListDescending();
return list.size() > maxSize ? list.subList(0, maxSize) : list;
}
synchronized List highestFirst() {
return getSortedListDescending();
}
synchronized List lowestFirst() {
return reversedList(getSortedListDescending());
}
synchronized List getSortedListDescending() {
List list = new ArrayList(map.keySet());
Collections.sort(list, new Comparator() {
public int compare(A a, A b) {
return map.get(b).compareTo(map.get(a));
}
});
return list;
}
synchronized int getNumberOfUniqueElements() {
return map.size();
}
synchronized int uniqueSize() {
return map.size();
}
synchronized Set asSet() {
return map.keySet();
}
synchronized NavigableSet navigableSet() {
return navigableKeys((NavigableMap) map);
}
synchronized Set keySet() {
return map.keySet();
}
synchronized A getMostPopularEntry() {
int max = 0;
A a = null;
for (Map.Entry entry : map.entrySet()) {
if (entry.getValue() > max) {
max = entry.getValue();
a = entry.getKey();
}
}
return a;
}
synchronized void removeAll(A key) {
size -= get(key);
map.remove(key);
}
synchronized int size() {
return size;
}
synchronized MultiSet mergeWith(MultiSet set) {
MultiSet result = new MultiSet();
for (A a : set.asSet()) {
result.add(a, set.get(a));
}
return result;
}
synchronized boolean isEmpty() {
return map.isEmpty();
}
synchronized public String toString() { // hmm. sync this?
return str(map);
}
synchronized void clear() {
map.clear();
size = 0;
}
final synchronized Map toMap(){ return asMap(); }
synchronized Map asMap() {
return cloneMap(map);
}
}
static class RemoteDB implements AutoCloseable {
DialogIO db;
String name;
// s = bot name or snippet ID
RemoteDB(String s) {
this(s, false);
}
RemoteDB(String s, boolean autoStart) {
name = s;
if (isSnippetID(s)) name = dbBotName(s);
db = findBot(name);
if (db == null)
if (autoStart) {
nohupJavax(fsI(s));
waitForBotStartUp(name);
assertNotNull("Weird problem", db = findBot(s));
} else
throw fail("DB " + s + " not running");
}
boolean functional() { return db != null; } // now always true
List list() { return adopt((List) rpc(db, "xlist")); }
List list(String className) { return adopt((List) rpc(db, "xlist", className)); }
List xlist() { return list(); }
List xlist(String className) { return list(className); }
// adopt is an internal method
List adopt(List l) {
if (l != null) for (RC rc : l) adopt(rc);
return l;
}
RC adopt(RC rc) { if (rc != null) rc.db = this; return rc; }
Object adopt(Object o) {
if (o instanceof RC) return adopt((RC) o);
return o;
}
String xclass(RC o) {
return (String) rpc(db, "xclass", o);
}
Object xget(RC o, String field) {
return adopt(rpc(db, "xget", o, field));
}
String xS(RC o, String field) {
return (String) xget(o, field);
}
RC xgetref(RC o, String field) {
return adopt((RC) xget(o, field));
}
void xset(RC o, String field, Object value) {
rpc(db, "xset", o, field, value);
}
RC uniq(String className) {
RC ref = first(list(className));
if (ref == null)
ref = xnew(className);
return ref;
}
RC xuniq(String className) { return uniq(className); }
RC xnew(String className, Object... values) {
return adopt((RC) rpc(db, "xnew", className, values));
}
void xdelete(RC o) {
rpc(db, "xdelete", o);
}
void xdelete(List l) {
rpc(db, "xdelete", l);
}
public void close() {
_close(db);
}
String fullgrab() { return (String) rpc(db, "xfullgrab"); }
String xfullgrab() { return fullgrab(); }
void xshutdown() { rpc(db, "xshutdown"); }
long xchangeCount() { return (long) rpc(db, "xchangeCount"); }
int xcount() { return (int) rpc(db, "xcount"); }
void reconnect() {
close();
db = findBot(name);
}
RC rc(long id) { return new RC(this, id); }
}
static class Pair implements Comparable> {
A a;
B b;
Pair() {}
Pair(A a, B b) {
this.b = b;
this.a = a;}
public int hashCode() {
return hashCodeFor(a) + 2*hashCodeFor(b);
}
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Pair)) return false;
Pair t = (Pair) o;
return eq(a, t.a) && eq(b, t.b);
}
public String toString() {
return "<" + a + ", " + b + ">";
}
public int compareTo(Pair p) {
if (p == null) return 1;
int i = ((Comparable) a).compareTo(p.a);
if (i != 0) return i;
return ((Comparable) b).compareTo(p.b);
}
}
static interface IMultiSet {
// returns new count
int add(A key);
/*void addAll(Iterable c) {
if (c != null) for (A a : c) add(a);
}
void addAll(MultiSet ms) {
for (A a : ms.keySet()) add(a, ms.get(a));
}*/
//int add(A key, int count);
//void put(A key, int count);
int get(A key);
//bool contains(A key);
//void remove(A key);
/*List topTen();
synchronized List getTopTen() { ret getTopTen(10); }
synchronized List getTopTen(int maxSize) {
List list = getSortedListDescending();
return list.size() > maxSize ? list.subList(0, maxSize) : list;
}
synchronized L highestFirst() {
ret getSortedListDescending();
}
synchronized L lowestFirst() {
ret reversedList(getSortedListDescending());
}
synchronized L getSortedListDescending() {
List list = new ArrayList(map.keySet());
Collections.sort(list, new Comparator() {
public int compare(A a, A b) {
return map.get(b).compareTo(map.get(a));
}
});
ret list;
}
synchronized int getNumberOfUniqueElements() {
return map.size();
}
synchronized int uniqueSize() {
ret map.size();
}
synchronized Set asSet() {
return map.keySet();
}
synchronized NavigableSet navigableSet() {
return navigableKeys((NavigableMap) map);
}
synchronized Set keySet() {
return map.keySet();
}
synchronized A getMostPopularEntry() {
int max = 0;
A a = null;
for (Map.Entry entry : map.entrySet()) {
if (entry.getValue() > max) {
max = entry.getValue();
a = entry.getKey();
}
}
return a;
}
synchronized void removeAll(A key) {
size -= get(key);
map.remove(key);
}
synchronized int size() {
ret size;
}
synchronized MultiSet mergeWith(MultiSet set) {
MultiSet result = new MultiSet();
for (A a : set.asSet()) {
result.add(a, set.get(a));
}
return result;
}
synchronized boolean isEmpty() {
return map.isEmpty();
}
synchronized public String toString() { // hmm. sync this?
return str(map);
}
synchronized void clear() {
map.clear();
size = 0;
}
synchronized Map asMap() {
ret cloneMap(map);
}*/
}
static abstract class DialogIO implements AutoCloseable {
String line;
boolean eos, loud, noClose;
Lock lock = lock();
abstract String readLineImpl();
abstract boolean isStillConnected();
abstract void sendLine(String line);
abstract boolean isLocalConnection();
abstract Socket getSocket();
int getPort() { Socket s = getSocket(); return s == null ? 0 : s.getPort(); }
boolean helloRead = false;
int shortenOutputTo = 500;
String readLineNoBlock() {
String l = line;
line = null;
return l;
}
boolean waitForLine() { try {
ping();
if (line != null) return true;
//print("Readline");
line = readLineImpl();
//print("Readline done: " + line);
if (line == null) eos = true;
return line != null;
} catch (Exception __e) { throw rethrow(__e); } }
String readLine() {
waitForLine();
helloRead = true;
return readLineNoBlock();
}
String ask(String s, Object... args) {
if (loud) return askLoudly(s, args);
if (!helloRead) readLine();
if (args.length != 0) s = format3(s, args);
sendLine(s);
return readLine();
}
String askLoudly(String s, Object... args) {
if (!helloRead) readLine();
if (args.length != 0) s = format3(s, args);
print("> " + shorten(s, shortenOutputTo));
sendLine(s);
String answer = readLine();
print("< " + shorten(answer, shortenOutputTo));
return answer;
}
void pushback(String l) {
if (line != null)
throw fail();
line = l;
helloRead = false;
}
}
static abstract class DialogHandler {
abstract void run(DialogIO io);
}
static Lock dbLock() {
return db_mainConcepts().lock;
}
static Lock dbLock(Concepts cc) {
return cc == null ? null : cc.lock;
}
static Lock dbLock(Concept c) {
return dbLock(c == null ? null : c._concepts);
}
static boolean bareDBMode_on = false;
static void bareDBMode() {
bareDBMode(null); // default autoSaveInterval
}
static void bareDBMode(Integer autoSaveInterval) {
bareDBMode_on = true;
conceptsAndBot(autoSaveInterval);
}
static ThreadLocal DynamicObject_loading = or((ThreadLocal) get(getClass("x30_pkg.x30_util"), "DynamicObject_loading"), new ThreadLocal());
static ThreadLocal dynamicObjectIsLoading_threadLocal() {
return DynamicObject_loading;
}
static List getAll(Map map, Collection l) {
return lookupAllOpt(map, l);
}
static List getAll(Collection l, Map map) {
return lookupAllOpt(map, l);
}
static > List getAll(Iterable l) {
return getVars(l);
}
static String shortClassName_dropNumberPrefix(Object o) {
return dropNumberPrefix(shortClassName(o));
}
static boolean eq(Object a, Object b) {
return a == b || a != null && b != null && a.equals(b);
}
// a little kludge for stuff like eq(symbol, "$X")
static boolean eq(Symbol a, String b) {
return eq(str(a), b);
}
static int boostHashCombine(int a, int b) {
return a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2));
}
static int _hashCode(Object a) {
return a == null ? 0 : a.hashCode();
}
static NavigableMap synchroTreeMap() {
return Collections.synchronizedNavigableMap(new TreeMap());
}
static Object _defaultClassFinder_value = defaultDefaultClassFinder();
static Object _defaultClassFinder() {
return _defaultClassFinder_value;
}
static List synchroList() {
return synchroList(new ArrayList());
}
static List synchroList(List l) {
return Collections.synchronizedList(l);
}
static boolean hasConcept(Class extends Concept> c, Object... params) {
return findConceptWhere(c, params) != null;
}
static String getDBProgramID_id;
static String getDBProgramID() {
return nempty(getDBProgramID_id) ? getDBProgramID_id : programIDWithCase();
}
static Object load(String varName) {
readLocally(varName);
return get(mc(), varName);
}
static Object load(String progID, String varName) {
readLocally(progID, varName);
return get(mc(), varName);
}
static boolean isFalse(Object o) {
return eq(false, o);
}
static Map> callF_cache = newDangerousWeakHashMap();
static A callF(F0 f) {
return f == null ? null : f.get();
}
static B callF(F1 f, A a) {
return f == null ? null : f.get(a);
}
static A callF(IF0 f) {
return f == null ? null : f.get();
}
static B callF(IF1 f, A a) {
return f == null ? null : f.get(a);
}
static B callF(A a, IF1 f) {
return f == null ? null : f.get(a);
}
static C callF(IF2 f, A a, B b) {
return f == null ? null : f.get(a, b);
}
static void callF(VF1 f, A a) {
if (f != null) f.get(a);
}
static void callF(A a, IVF1 f) {
if (f != null) f.get(a);
}
static void callF(IVF1 f, A a) {
if (f != null) f.get(a);
}
static Object callF(Runnable r) { { if (r != null) r.run(); } return null; }
static Object callF(Object f, Object... args) {
return safeCallF(f, args);
}
static Object safeCallF(Object f, Object... args) {
if (f instanceof Runnable) {
((Runnable) f).run();
return null;
}
if (f == null) return null;
Class c = f.getClass();
ArrayList methods;
synchronized(callF_cache) {
methods = callF_cache.get(c);
if (methods == null)
methods = callF_makeCache(c);
}
int n = l(methods);
if (n == 0) {
throw fail("No get method in " + getClassName(c));
}
if (n == 1) return invokeMethod(methods.get(0), f, args);
for (int i = 0; i < n; i++) {
Method m = methods.get(i);
if (call_checkArgs(m, args, false))
return invokeMethod(m, f, args);
}
throw fail("No matching get method in " + getClassName(c));
}
// used internally
static ArrayList callF_makeCache(Class c) {
ArrayList l = new ArrayList();
Class _c = c;
do {
for (Method m : _c.getDeclaredMethods())
if (m.getName().equals("get")) {
makeAccessible(m);
l.add(m);
}
if (!l.isEmpty()) break;
_c = _c.getSuperclass();
} while (_c != null);
callF_cache.put(c, l);
return l;
}
static boolean exceptionMessageContains(Throwable e, String s) {
return cic(getInnerMessage(e), s);
}
static void printShortException(Throwable e) {
print(exceptionToStringShort(e));
}
static void printShortException(String s, Throwable e) {
print(s, exceptionToStringShort(e));
}
static volatile StringBuffer local_log = new StringBuffer(); // not redirected
static boolean printAlsoToSystemOut = true;
static volatile Appendable print_log = local_log; // might be redirected, e.g. to main bot
// in bytes - will cut to half that
static volatile int print_log_max = 1024*1024;
static volatile int local_log_max = 100*1024;
static boolean print_silent = false; // total mute if set
static Object print_byThread_lock = new Object();
static volatile ThreadLocal]