<> <> <> DIRECTORY MonitoredQueue, NodeProps, Process, RopeFrom, TiogaLocks, TiogaLooks, TiogaLooksOps, TiogaNode, TiogaItemClass, TiogaBasicClass, TiogaNodeOps, Rope, RopeEdit; TiogaNodeOpsImpl: CEDAR PROGRAM IMPORTS MonitoredQueue, NodeProps, Process, RopeEdit, RopeFrom, TiogaLocks, TiogaLooksOps EXPORTS TiogaNodeOps SHARES TiogaNode = BEGIN OPEN TiogaNodeOps, TiogaNode, TiogaItemClass, TiogaBasicClass; ROPE: TYPE = Rope.ROPE; <<***** Item Class registration>> ItemClasses: TYPE = RECORD[SEQUENCE maxItems: ItemClassID OF ItemClass]; items: REF ItemClasses _ NEW[ItemClasses[4]]; itemsRegistered: ItemClassID _ 0; RegisterItemClass: PUBLIC PROC [classRec: ItemClassRec, OKToOverwritePrevious: BOOL --_ FALSE--] RETURNS [ItemClassID] = BEGIN class: ItemClass _ NEW[ItemClassRec _ classRec]; FOR i: ItemClassID IN [0..itemsRegistered) DO IF items[i].flavor=classRec.flavor THEN BEGIN --re-registration IF NOT OKToOverwritePrevious THEN ERROR; items[i] _ class; RETURN [i]; END; ENDLOOP; IF itemsRegistered >= items.maxItems THEN BEGIN -- expand class array newItems: REF ItemClasses; IF itemsRegistered+1 >= invalidItemID THEN ERROR; -- too many items registered newItems _ NEW[ItemClasses[items.maxItems*2]]; FOR i: ItemClassID IN [0..itemsRegistered) DO newItems[i] _ items[i]; ENDLOOP; items _ newItems; END; items[itemsRegistered] _ class; itemsRegistered _ itemsRegistered+1; RETURN[itemsRegistered-1]; END; LookupItemID: PUBLIC PROC [flavor: ItemFlavor, buildNewClassIfLookupFails: BOOL --_ FALSE--] RETURNS [ItemClassID] = BEGIN <> FOR i: ItemClassID IN [0..itemsRegistered) DO IF items[i].flavor=flavor THEN RETURN [i]; ENDLOOP; IF buildNewClassIfLookupFails THEN ERROR; --Sorry, not yet implemented. RETURN[invalidItemID]; END; <<>> FetchItemClass: PUBLIC PROC [id: ItemClassID] RETURNS [ItemClass] = BEGIN RETURN[IF id>=itemsRegistered THEN NIL ELSE items[id]]; END; <<>> <<>> <<***** Basic Class registration>> BasicClasses: TYPE = RECORD[SEQUENCE maxBasics: BasicClassID OF BasicClass]; basics: REF BasicClasses _ NEW[BasicClasses[4]]; basicsRegistered: BasicClassID _ 0; RegisterBasicClass: PUBLIC PROC [classRec: BasicClassRec, OKToOverwritePrevious: BOOL --_ FALSE--] RETURNS [BasicClassID] = BEGIN class: BasicClass _ NEW[BasicClassRec _ classRec]; FOR i: BasicClassID IN [0..basicsRegistered) DO IF basics[i].flavor=classRec.flavor THEN BEGIN -- re-registration IF NOT OKToOverwritePrevious THEN ERROR; basics[i] _ class; RETURN [i]; END; ENDLOOP; IF basicsRegistered >= basics.maxBasics THEN BEGIN -- expand class array newBasics: REF BasicClasses; IF basicsRegistered+1 >= invalidBasicID THEN ERROR; -- too many basics registered newBasics _ NEW[BasicClasses[basics.maxBasics*2]]; FOR i: BasicClassID IN [0..basicsRegistered) DO newBasics[i] _ basics[i]; ENDLOOP; basics _ newBasics; END; basics[basicsRegistered] _ class; basicsRegistered _ basicsRegistered+1; RETURN[basicsRegistered-1]; END; LookupBasicID: PUBLIC PROC [flavor: BasicFlavor, buildNewClassIfLookupFails: BOOL --_ FALSE--] RETURNS [BasicClassID] = BEGIN <> FOR i: BasicClassID IN [0..basicsRegistered) DO IF basics[i].flavor=flavor THEN RETURN [i]; ENDLOOP; IF buildNewClassIfLookupFails THEN ERROR; --Sorry, not yet implemented. RETURN[invalidBasicID]; END; <<>> FetchBasicClass: PUBLIC PROC [id: BasicClassID] RETURNS [BasicClass] = BEGIN RETURN[IF id>=basicsRegistered THEN NIL ELSE basics[id]]; END; <<>> <<>> <<***** Node creation>> NewBranchNode: PUBLIC PROC RETURNS [br: RefBranchNode] = BEGIN br _ NEW[branch Node]; br.last _ TRUE; END; NewTextNode: PUBLIC PROC [class: ItemClassID] RETURNS [tx: RefTextNode] = BEGIN itemClass: TiogaItemClass.ItemClass _ FetchItemClass[class]; tx _ NEW[text item Node]; tx.last _ TRUE; tx.class _ class; IF itemClass.init#NIL THEN itemClass.init[tx]; END; NewListNode: PUBLIC PROC [class: ItemClassID] RETURNS [ls: RefListNode] = BEGIN itemClass: TiogaItemClass.ItemClass _ FetchItemClass[class]; ls _ NEW[list item Node]; ls.last _ TRUE; ls.class _ class; IF itemClass.init#NIL THEN itemClass.init[ls]; END; NewBoxNode: PUBLIC PROC [class: ItemClassID] RETURNS [bx: RefBoxNode] = BEGIN itemClass: TiogaItemClass.ItemClass _ FetchItemClass[class]; bx _ NEW[box item Node]; bx.last _ TRUE; bx.class _ class; IF itemClass.init#NIL THEN itemClass.init[bx]; END; NewBasicNode: PUBLIC PROC [class: BasicClassID] RETURNS [bs: RefBasicNode] = BEGIN basicClass: TiogaBasicClass.BasicClass _ FetchBasicClass[class]; bs _ NEW[basic Node]; bs.last _ TRUE; bs.class _ class; IF basicClass.init#NIL THEN basicClass.init[bs]; END; <<>> <<***** Text node operations>> <<>> Fetch: PUBLIC PROC [text: RefTextNode, index: Offset] RETURNS [CHAR, TiogaLooks.Looks] = { <> <> RETURN [RopeEdit.Fetch[text.rope,index], TiogaLooksOps.FetchLooks[text.runs,index]] }; FetchChar: PUBLIC PROC [text: RefTextNode, index: Offset] RETURNS [CHAR] = { <> <> RETURN [RopeEdit.Fetch[text.rope,index]] }; FetchLooks: PUBLIC PROC [text: RefTextNode, index: Offset] RETURNS [TiogaLooks.Looks] = { <> <> RETURN [TiogaLooksOps.FetchLooks[text.runs,index]] }; GetRope: PUBLIC PROC [text: RefTextNode] RETURNS [ROPE] = { RETURN [IF text=NIL THEN NIL ELSE text.rope] }; GetRuns: PUBLIC PROC [text: RefTextNode] RETURNS [TiogaLooks.Runs] = { RETURN [IF text=NIL THEN NIL ELSE text.runs] }; Size: PUBLIC PROC [text: RefTextNode] RETURNS [Offset] = { RETURN [IF text=NIL THEN 0 ELSE RopeEdit.Size[text.rope]] }; <<***** Operation to free a tree by clearing all REF's>> treeQueue: MonitoredQueue.MQ _ MonitoredQueue.Create[]; FreeTree: PUBLIC PROC [root: RefBranchNode] = { IF root=NIL OR root.deleted THEN RETURN; root.deleted _ TRUE; <> <> <> < IF x.root = root THEN RETURN;>> < IF x.root = root THEN RETURN;>> < IF x.destRoot = root OR x.sourceRoot = root THEN RETURN;>> < IF x.root = root THEN RETURN;>> < IF x.root = root THEN RETURN;>> <> <> <> MonitoredQueue.Add[root, treeQueue] }; FreeTrees: PROC = { DO -- forever tree: REF ANY _ MonitoredQueue.Remove[treeQueue]; DoFreeTree[NARROW[tree] ! ABORTED => CONTINUE]; ENDLOOP }; nodesFreed: INT _ 0; DoFreeTree: PROC [root: RefBranchNode] = { itemClassID: ItemClassID _ 0; itemClass: TiogaItemClass.ItemClass _ FetchItemClass[itemClassID]; basicClassID: BasicClassID _ 0; basicClass: TiogaBasicClass.BasicClass _ FetchBasicClass[basicClassID]; DestroyItem: PROC [item: RefItemNode] = INLINE { IF item.class # itemClassID THEN itemClass _ FetchItemClass[itemClassID _ item.class]; IF itemClass.destroy # NIL THEN itemClass.destroy[item] }; DestroyBasic: PROC [basic: RefBasicNode] = INLINE { IF basic.class # basicClassID THEN basicClass _ FetchBasicClass[basicClassID _ basic.class]; IF basicClass.destroy # NIL THEN basicClass.destroy[basic] }; next, node: Ref; IF root.child = NIL THEN RETURN; -- has already been freed TRUSTED {Process.SetPriority[Process.priorityBackground]}; -- no rush to finish this Process.Yield[]; [] _ TiogaLocks.Lock[root, "DoFreeTree", write]; -- must make sure no one still reading it. never unlock it. node _ root; next _ NIL; DO -- go through the tree zapping REF's WITH node SELECT FROM br: RefBranchNode => IF br.contents # NIL THEN { next _ br.contents; br.contents _ NIL } ELSE { next _ br.child; br.child _ NIL }; bx: RefBoxNode => next _ bx.contents; ls: RefListNode => next _ ls.contents; ENDCASE; IF next # NIL THEN { node _ next; next _ NIL; LOOP }; next _ node.next; node.next _ NIL; node.last _ node.deleted _ TRUE; <> WITH node SELECT FROM tx: RefTextNode => { DestroyItem[tx]; tx.rope _ NIL; tx.runs _ NIL }; bx: RefBoxNode => { DestroyItem[bx]; bx.data _ NIL }; ls: RefListNode => { DestroyItem[ls]; ls.data _ NIL }; bc: RefBasicNode => { DestroyBasic[bc]; bc.data _ NIL }; ENDCASE; IF node.hasPropList THEN NodeProps.RemProps[node]; nodesFreed _ nodesFreed+1; IF (node _ next) = NIL THEN EXIT; ENDLOOP }; <<***** Operations to create a text node from a rope or a string>> FromRope: PUBLIC PROC [rope: ROPE, class: ItemClassID _ defaultTextClassID] RETURNS [new: RefTextNode] = { <> new _ NewTextNode[class]; new.rope _ rope; new.last _ TRUE; new.runs _ TiogaLooksOps.CreateRun[RopeEdit.Size[rope]] }; FromString: PUBLIC PROC [ string: REF READONLY TEXT, class: ItemClassID _ defaultTextClassID] RETURNS [new: RefTextNode] = { <> new _ NewTextNode[class]; new.last _ TRUE; new.rope _ RopeFrom.String[string]; new.runs _ TiogaLooksOps.CreateRun[RopeEdit.Size[new.rope]] }; <<***** Operation to create a document from an item node>> DocFromNode: PUBLIC PROC [child: RefItemNode] RETURNS [root: RefBranchNode] = { br: TiogaNode.RefBranchNode _ NewBranchNode[]; root _ NewBranchNode[]; root.child _ br; root.last _ TRUE; br.contents _ child; br.last _ TRUE; br.next _ root; child.last _ TRUE; child.next _ br; }; <<***** Miscellaneous>> MakeNodeLoc: PUBLIC PROC [n: Ref] RETURNS [Location] = { RETURN [[[n, NIL], NodeItself]] }; MakeNodeSpan: PUBLIC PROC [first, last: Ref] RETURNS [Span] = { RETURN [[MakeNodeLoc[first], MakeNodeLoc[last]]] }; EndPos: PUBLIC PROC [n: RefTextNode] RETURNS [Offset] = {RETURN[IF n=NIL THEN 0 ELSE MAX[RopeEdit.Size[n.rope],1]-1]}; END.