sclass WebNode { Web web; new L labels; *() {} *(Web *web) { web.fireNewNode(this); } void addLabel(S label) { if (setAdd(labels, label) && web != null) { web.index(label, this); ++web.size; } } bool hasLabel(S label) { ret labels.contains(label); } toString { ret str(labels); } int count() { ret 1 + l(labels); } } sclass Web { new L nodes; Map, WebNode> relations = new HashMap; new MultiMap index; // label -> node Map> pots = new Map; transient Lock lock = reentrantLock(true); int size; static new L onNewNode; // L static new L onNewLabel; // L bool potNotEmpty(S pot) { ret nempty(getPot(pot)); } L clearPot(S pot) { L l = getPot(pot); L l2 = cloneList(l); l.clear(); ret l2; } L getPot(S pot) { L l = pots.get(pot); if (l == null) pots.put(pot, l = cloneList(nodes)); ret l; } void relation(S a, S arrow, S b) { getRelation(a, b).addLabel(arrow); } WebNode getRelation(S a, S b) { ret getRelation(findNode(a), findNode(b)); } WebNode getRelation(WebNode a, WebNode b) { Pair p = pair(a, b); WebNode r = relations.get(p); if (r == null) relations.put(p, r = newNode()); ret r; } WebNode newNode() { WebNode node = new WebNode(this); nodes.add(node); ++size; for (L l : values(pots)) l.add(node); ret node; } WebNode node(S s) { ret findNode(s); } WebNode findNode(S s) { WebNode n = findNodeOpt(s); ret n != null ? n : newNode(s); } WebNode findNodeOpt(S s) { ret first(index.get(s)); } WebNode newNode(S... labels) { WebNode n = newNode(); for (S label : labels) n.addLabel(label); ret n; } toString { ret webToString(this); } int count() { ret size; /*int count = 0; for (WebNode n : nodes) count += n.count(); for (WebNode n : values(relations)) count += n.count(); ret count;*/ } void index(S label, WebNode n) { index.put(label, n); fireNewLabel(n, label); } void clear { size = 0; clearAll(nodes, relations, index, pots); } void fireNewNode(WebNode node) { for (O f : onNewNode) pcallF(f, node); } void fireNewLabel(WebNode node, S label) { for (O f : onNewLabel) pcallF(f, node, label); } }