1 MultiLevelIdentityDictionary : Collection
10 dictionary = this.newInternalNode;
13 newInternalNode { ^this.nodeType.new }
22 atPathFail { arg path, function;
27 if (item.isNil, { ^function.value });
32 ^this.atPathFail(path)
38 ^this.putAtPath(path, item);
40 putAtPath { arg path, val;
46 item = item.atFail(name, {
48 newitem = this.newInternalNode;
49 item.put(name, newitem);
53 item.put(lastName, val);
56 create { arg ... args;
60 item = item.atFail(name, {
62 newitem = this.newInternalNode;
63 item.put(name, newitem);
69 choose { arg ... start;
74 item = this.at(*start);
76 Error("Library-choose start address not found: " ++ start).throw;
79 ^this.prChooseFrom(item);
81 putTree { arg ... items;
82 this.prPutTree([],items)
84 postTree { arg obj,tabs=0;
85 if(obj.isNil,{ obj = dictionary });
86 if(obj.isKindOf(this.nodeType),{
88 obj.keysValuesDo({ arg k,v;
89 tabs.do({ Char.tab.post });
92 this.postTree(v,tabs + 1)
100 dictionary.do(function);
102 // remove only the leaf node indicated by path
103 // parent nodes remain in the MLID even if they are empty
106 ^this.removeAtPath(path)
108 removeAtPath { arg path;
114 item = item.at(name);
115 if (item.isNil, { ^nil });
117 ^item.removeAt(lastName);
119 // remove the leaf node
120 // as well as parent nodes that become empty after removing the child
121 // slower but leaves less cruft in the tree
122 prRemoveAtPathRecursive { |path, i = 0, item|
123 var name = path[i], result;
124 if(item[name].isNil) { ^nil };
125 if(i < (path.size-1)) {
126 result = this.prRemoveAtPathRecursive(path, i+1, item[name]);
127 (item[name].isEmpty).if({ item.removeAt(name) });
133 removeEmptyAtPath { arg path;
134 ^this.prRemoveAtPathRecursive(path, 0, dictionary)
136 removeEmptyAt { arg ...path;
137 ^this.prRemoveAtPathRecursive(path, 0, dictionary);
142 this.put(assn.key, assn.value);
144 remove { ^this.shouldNotImplement(thisMethod) }
145 removeFail { ^this.shouldNotImplement(thisMethod) }
147 prChooseFrom { arg dict;
150 if(item.isKindOf(this.nodeType),{
151 ^this.prChooseFrom(item);
156 prPutTree { arg keys,items;
157 forBy(0,items.size - 1,2,{ arg i;
160 item = items.at(i + 1);
161 if(item.isArray.not,{
162 this.put(* keys ++ [key,item]);
165 this.prPutTree(keys ++ [key],item);
169 leaves { arg startAt;
171 startAt = dictionary;
173 startAt = this.at(*startAt);
175 ^this.prNestedValuesFromDict(startAt);
177 prNestedValuesFromDict { arg dict;
178 ^dict.values.collect({ arg thing;
179 if(thing.isKindOf(this.nodeType),{
180 this.prNestedValuesFromDict(thing)
187 // Tree-like do methods
191 this.doLeafDo([], this.dictionary, func);
194 arg folderpath, func;
197 folderpath = folderpath.asArray;
199 folder = this.atPath(folderpath);
200 if (folder.notNil && folder.isKindOf(this.nodeType), {
201 this.doLeafDo(folderpath, folder, func);
206 arg path, object, func;
208 if (object.isKindOf(this.nodeType), {
209 object.keysValuesDo({
211 this.doLeafDo(path ++ [name], subobject, func)
214 func.value(path, object);
219 arg branchFunc, leafFunc, argument0, postBranchFunc;
222 result = this.doTreeDo([], this.dictionary, branchFunc, leafFunc, argument0, postBranchFunc);
226 arg folderpath, branchFunc, leafFunc, argument0, postBranchFunc;
229 folderpath = folderpath.asArray;
231 folder = this.atPath(folderpath);
232 if (folder.isKindOf(this.nodeType), {
233 result = this.doTreeDo(folderpath, folder, branchFunc, leafFunc, argument0, postBranchFunc);
242 arg path, object, branchFunc, leafFunc, argument, postBranchFunc;
245 if (object.isKindOf(this.nodeType), {
246 if (branchFunc.notNil, {
247 result = branchFunc.value(path, object, argument);
251 object.keysValuesDo({
253 this.doTreeDo(path ++ [name], subobject, branchFunc, leafFunc, result, postBranchFunc)
255 if (postBranchFunc.notNil, {
256 result = postBranchFunc.value(path, object, result);
260 leafFunc.value(path, object, argument);
264 treeCollect { arg branchFunc, leafFunc, postBranchFunc;
266 result = this.doTreeCollect([], this.dictionary, branchFunc, leafFunc, postBranchFunc);
269 doTreeCollect { arg path, object, branchFunc, leafFunc, postBranchFunc;
270 var confirm, collection, result;
272 if (object.isKindOf(this.nodeType), {
273 if (branchFunc.notNil, {
274 #confirm, result = branchFunc.value(path, object);
276 #confirm, result = [true, nil]
280 object.keysValuesDo({
282 collection = collection.add(this.doTreeCollect(path ++ [name], subobject,
283 branchFunc, leafFunc, postBranchFunc));
285 collection.removeAllSuchThat({arg item; item.isNil});
286 if (postBranchFunc.notNil, {
287 result = postBranchFunc.value(path, object, collection);
296 ^leafFunc.value(path, object)
301 arg branchFunc, leafFunc, argument0, postBranchFunc, sortFunc;
304 result = this.doSortedTreeDo([], this.dictionary, branchFunc, leafFunc, argument0, postBranchFunc, sortFunc);
308 arg path, object, branchFunc, leafFunc, argument, postBranchFunc, sortFunc;
311 sortFunc = sortFunc ? {arg a, b; a < b};
313 if (object.isKindOf(this.nodeType), {
314 if (branchFunc.notNil, {
315 result = branchFunc.value(path, object, argument);
319 object.sortedKeysValuesDo({
321 this.doSortedTreeDo(path ++ [name], subobject, branchFunc, leafFunc, result, postBranchFunc, sortFunc)
323 if (postBranchFunc.notNil, {
324 postBranchFunc.value(path, object, result);
328 leafFunc.value(path, object, argument);
333 arg folderpath, function;
336 folderpath = folderpath.asArray;
338 folder = this.atPath(folderpath);
339 if (folder.notNil && folder.isKindOf(this.nodeType), {
340 folder.keysValuesDo({
342 if (object.isKindOf(this.nodeType).not, {
343 function.value(folderpath ++ [name], object);
349 storeOn { arg stream;
350 stream << this.class.name << "[" <<<* dictionary << "]"
353 printOn { arg stream;
354 stream << this.class.name << "[" <<* dictionary << "]"
360 LibraryBase : MultiLevelIdentityDictionary
363 ^this.subclassResponsibility(thisMethod);
366 ^this.subclassResponsibility(thisMethod);
371 this.global = this.new;
374 ^this.global.at(*args);
378 ^this.global.at(*args)
381 ^this.global.put(*args)
385 ^this.global.put(*args)
387 *create { arg ... args;
388 ^this.global.create(*args)
397 Library : LibraryBase
402 *global_ { arg obj; global = obj; }
409 Archive : LibraryBase
412 classvar <>archiveDir;
415 *global_ { arg obj; global = obj; }
419 archiveDir = Platform.userAppSupportDir;
422 *read { arg filename;
423 var expandedFileName;
424 expandedFileName = filename ?? (archiveDir ++ "/archive.sctxar");
425 if (File.exists(expandedFileName)) {
426 if (expandedFileName.endsWith(".scar")) {
427 global = this.readBinaryArchive(expandedFileName);
429 global = this.readArchive(expandedFileName);
436 *write { arg filename;
437 var expandedFileName;
438 expandedFileName = filename ?? (archiveDir ++ "/archive.sctxar");
439 global.writeArchive(expandedFileName);