TextNodeImpl.mesa
Copyright Ó 1985, 1986, 1987, 1988, 1991, 1992 by Xerox Corporation. All rights reserved.
written by Bill Paxton, March 1981
last edit by Bill Paxton, August 11, 1982 9:51 am
Rick Beach, March 27, 1985 10:13:12 am PST
Michael Plass, September 24, 1991 2:21 pm PDT
Doug Terry, August 9, 1988 3:28:42 pm PDT
Don Baker, June 3, 1988 4:24:24 pm PDT
Bier, January 6, 1989 5:14:03 pm PST
Doug Wyatt, July 21, 1992 4:37 pm PDT
This is a modified version (by Doug Terry) of TextNodeImpl.mesa that performs transformations on "active" nodes as they are requested. Active nodes as those with the "Active" property; the value associated with this property identifies a particular transformation. Transformations must be registered using TextNodeRegistry.
Note: All of the public procedures in this module assume that any nodes passed as inputs are regular text nodes (i.e. do not need to be transformed) and ensure that any nodes returned as results are regular text nodes. Strange behavior might result if the transformation of an active node yields another active node.
TextNodeRegistryImpl
ProcSet: TYPE ~ TextNodeRegistry.ProcSet;
NotifySet: TYPE ~ TextNodeRegistry.NotifySet;
NotifyProc: TYPE ~ TextNodeRegistry.NotifyProc;
registrationTable: RefTab.Ref = RefTab.Create[];
notificationTable: RefTab.Ref = RefTab.Create[];
Register:
PUBLIC
PROC [activity:
ATOM, procs: ProcSet]
RETURNS [] ~ {
The given TransformProc will hereafter be applied to all nodes having an active property with value=activity. Notification procedures are invoked accordingly.
NotifyRegistration: RefTab.EachPairAction = {
[key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL ← FALSE]
IF val#
NIL
THEN {
notifier: NotifyProc = NARROW[val, NotifySet].registrationNotify;
IF notifier#NIL THEN notifier[key, activity, procs.activityOn];
};
};
[] ¬ RefTab.Store[registrationTable, activity, procs];
[] ¬ RefTab.Pairs[notificationTable, NotifyRegistration];
};
UnRegister:
PUBLIC
PROC [activity:
ATOM]
RETURNS [] ~ {
Removes the procedures associated with the given activity. Notification procedures are invoked accordingly.
on: BOOLEAN = IsActivityOn[activity];
NotifyUnRegistration: RefTab.EachPairAction = {
[key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL ← FALSE]
IF val#
NIL
THEN {
notifier: NotifyProc = NARROW[val, NotifySet].unRegistrationNotify;
IF notifier#NIL THEN notifier[key, activity, on];
};
};
[] ¬ RefTab.Pairs[notificationTable, NotifyUnRegistration];
[] ¬ RefTab.Delete[registrationTable, activity];
};
GetProcs:
PUBLIC
PROC [activity:
ATOM]
RETURNS [procs: ProcSet] ~ {
Return the procedures associated with the given activity.
RETURN[NARROW[RefTab.Fetch[registrationTable, activity].val]];
};
ActivityOn:
PUBLIC
PROC [activity:
ATOM, on:
BOOL ¬
TRUE]
RETURNS [wasOn:
BOOL] ~ {
Determines whether or not the registered transformations should be currently applied; if activity=NIL then activity is turned on/off for all registered clients. Registered clients are informed via callbacks when this switch is toggled so they can take appropriate action.
SetActivity: RefTab.EachPairAction = {
[key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL ← FALSE]
enumeratedActivity: ATOM = NARROW[key];
procs: ProcSet = NARROW[val];
NotifyActivityOn: RefTab.EachPairAction = {
[key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL ← FALSE]
notifier: NotifyProc = NARROW[val, NotifySet].activityOnNotify;
IF notifier#NIL THEN notifier[key, enumeratedActivity, on];
};
IF procs#
NIL
THEN {
wasOn ¬ procs.activityOn;
procs.activityOn ¬ on;
IF wasOn#on
THEN {
IF procs.activityProc#
NIL
THEN
procs.activityProc[on, procs.activityProcData];
[] ¬ RefTab.Pairs[notificationTable, NotifyActivityOn];
};
};
};
wasOn ¬ FALSE;
IF activity#
NIL
THEN [] ¬ SetActivity[activity, RefTab.Fetch[registrationTable, activity].val]
ELSE [] ¬ RefTab.Pairs[registrationTable, SetActivity];
};
IsActivityOn:
PUBLIC
PROC [activity:
ATOM]
RETURNS [isOn:
BOOLEAN] ~ {
Returns true if the activity specified is on.
RETURN[NARROW[RefTab.Fetch[registrationTable, activity].val, ProcSet].activityOn];
};
NotificationRegister:
PUBLIC
PROC [handle:
REF
ANY, notifyProcs: NotifySet]
RETURNS [] ~ {
Registers a set of notification procedures which are called both in the event of a change in activity registration or status *and* during this call, notification will be given of the "registration" of those activities which are already registered.
notifier: NotifyProc;
NotifyAllRegister: RefTab.EachPairAction = {
[key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL ← FALSE]
IF val#NIL THEN notifier[handle, NARROW[key], NARROW[val, ProcSet].activityOn];
};
[] ¬ RefTab.Store[notificationTable, handle, notifyProcs];
IF notifyProcs#
NIL
THEN {
notifier ¬ notifyProcs.registrationNotify;
IF notifier#NIL THEN [] ¬ RefTab.Pairs[registrationTable, NotifyAllRegister];
};
};
NotificationUnRegister:
PUBLIC
PROC [handle:
REF
ANY, doNotify:
BOOLEAN ¬
FALSE]
RETURNS [] ~ {
Unregisters a set of notification procedures. If doNotify is set, notification of "unregistration" of registered activities will be made--during this call.
notifier: NotifyProc;
NotifyAllUnRegister: RefTab.EachPairAction = {
[key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL ← FALSE]
IF val#NIL THEN notifier[handle, NARROW[key], NARROW[val, ProcSet].activityOn];
};
notifyProcs: NotifySet = NARROW[RefTab.Fetch[notificationTable, handle].val];
[] ¬ RefTab.Delete[notificationTable, handle];
IF doNotify
AND notifyProcs#
NIL
THEN {
notifier ¬ notifyProcs.unRegistrationNotify;
IF notifier#NIL THEN [] ¬ RefTab.Pairs[registrationTable, NotifyAllUnRegister];
};
};
DoTransform:
PUBLIC
PROC [activity:
ATOM, node: Node, parent: Node ¬
NIL, wantFirst:
BOOL ¬
TRUE]
RETURNS [new: Node] ~ {
Force the TransformProc associated with the given activity to be applied to the node. (This is intended primarily for use by TextNodeImpl.)
procs: ProcSet = NARROW[RefTab.Fetch[registrationTable, activity].val];
new ¬ IF procs=NIL OR NOT procs.activityOn OR procs.transformProc=NIL THEN node ELSE procs.transformProc[node, parent, wantFirst, procs.transformProcData];
};
GetSize:
PUBLIC
PROC [activity:
ATOM, node: Node]
RETURNS [size:
INT] ~ {
Computes or estimates the size that the given node will be AFTER it is transformed by the specified activity. (This is intended primarily for use by TextNodeImpl.)
procs: ProcSet = NARROW[RefTab.Fetch[registrationTable, activity].val];
size ¬ IF procs=NIL OR NOT procs.activityOn OR procs.sizeProc=NIL THEN Rope.Size[node.rope] ELSE procs.sizeProc[node, procs.sizeProcData];
};
The rest of TextNodeImpl
NewTextNode:
PUBLIC
PROC
RETURNS [Node] = {
RETURN[NEW[Tioga.NodeRep ¬ []]];
};
XParent:
PROC [n: Node]
RETURNS [Node] ~
INLINE {
RETURN[n.parent] };
XNext:
PROC [n: Node]
RETURNS [Node] ~
INLINE {
RETURN[n.next] };
XChild:
PROC [n: Node]
RETURNS [Node] ~
INLINE {
RETURN[n.child] };
XRoot:
PROC [n: Node]
RETURNS [root: Node ¬
NIL] ~ {
FOR x: Node ¬ n, XParent[x] UNTIL x=NIL DO root ¬ x ENDLOOP;
};
XFirstSibling:
PROC [n: Node]
RETURNS [Node] ~ {
p: Node ~ XParent[n]; RETURN[IF p=NIL THEN n ELSE XChild[p]];
};
XPrevious:
PROC [n: Node]
RETURNS [prev: Node ¬
NIL] ~ {
FOR x: Node ¬ XFirstSibling[n], XNext[x] UNTIL x=n DO prev ¬ x ENDLOOP;
};
XForward:
PROC [n: Node]
RETURNS [nx: Node, deltaLevel:
INTEGER] ~ {
child: Node ~ XChild[n];
IF child#
NIL
THEN RETURN[child, 1] -- descend in the tree
ELSE {
-- move to sibling or up* then sibling
x: Node ¬ n;
FOR delta:
INTEGER ¬ 0, delta-1
DO
next: Node ~ XNext[x];
IF next#NIL THEN RETURN[next, delta]; -- the sibling
IF (x ¬ XParent[x])=NIL THEN RETURN[NIL, delta]; -- the parent
ENDLOOP;
};
};
Parent:
PUBLIC
PROC [n: Node]
RETURNS [Node] ~ {
RETURN[IF n=NIL THEN NIL ELSE Transform[node: XParent[n], wantFirst: FALSE]];
};
Root:
PUBLIC
PROC [n: Node]
RETURNS [Node] ~ {
RETURN[IF n=NIL THEN NIL ELSE Transform[node: XRoot[n], wantFirst: TRUE]];
};
Level:
PUBLIC
PROC [n: Node]
RETURNS [level:
INTEGER ¬ 0] ~ {
p: Node ¬ IF n=NIL THEN NIL ELSE XParent[n];
UNTIL p=NIL DO level ¬ level+1; p ¬ XParent[p] ENDLOOP;
};
Next:
PUBLIC
PROC [n: Node]
RETURNS [Node] ~ {
RETURN[IF n=NIL THEN NIL ELSE Transform[node: XNext[n], wantFirst: TRUE]];
};
Previous:
PUBLIC
PROC [n: Node, parent: Node ¬
NIL]
RETURNS [nx: Node] ~ {
RETURN[IF n=NIL THEN NIL ELSE Transform[node: XPrevious[n], wantFirst: FALSE]];
};
FirstChild:
PUBLIC
PROC [n: Node]
RETURNS [Node] ~ {
RETURN[IF n=NIL THEN NIL ELSE Transform[node: XChild[n], wantFirst: TRUE]];
};
Forward:
PUBLIC
PROC [node: Node]
RETURNS [nx: Node, levelDelta:
INTEGER] ~ {
IF node=NIL THEN RETURN[NIL, 0];
[nx, levelDelta] ¬ XForward[node];
nx ¬ Transform[node: nx, wantFirst: TRUE];
};
StepForward:
PUBLIC
PROC [node: Node]
RETURNS [Node] ~ {
RETURN[Forward[node].nx];
};
Backward:
PUBLIC
PROC [node: Node, parent: Node ¬
NIL]
RETURNS [back, backparent: Node, levelDelta: INTEGER] = {
returns preceding node in standard tree walk order
child, child2: Node;
IF node=NIL OR (parent ¬ XParent[node])=NIL THEN RETURN [NIL, NIL, 0];
IF (child ¬ parent.child) = node THEN RETURN [Transform[node: parent, wantFirst: FALSE], Parent[parent], -1];
DO
IF child.next=
NIL
THEN
ERROR;
-- incorrect value supplied for parent
IF (child2 ¬ child.next)=node THEN EXIT;
child ¬ child2;
ENDLOOP;
levelDelta ¬ 0;
child ¬ Transform[node: child, wantFirst: FALSE];
DO
IF (child2 ¬ LastChild[child]) =
NIL
THEN
RETURN [child, parent, levelDelta];
levelDelta ¬ levelDelta+1;
parent ¬ child; child ¬ child2;
ENDLOOP;
};
StepBackward:
PUBLIC
PROC [node: Node, parent: Node ¬
NIL]
RETURNS [Node] ~ {
RETURN[Backward[node, parent].back];
};
ForwardClipped:
PUBLIC
PROC [node: Node, maxLevel:
INTEGER,
nodeLevel:
INTEGER ¬ 0]
RETURNS [nx: Node, nxLevel:
INTEGER] = {
like Forward, but limits how deep will go in tree
if pass nodeLevel=0, correct value will be computed
nxLevel = Level[nx] <= MAX[maxLevel, Level[node]]
child: Node;
IF node=NIL THEN RETURN [NIL, 0];
IF nodeLevel <= 0 THEN nodeLevel ¬ Level[node];
IF nodeLevel < maxLevel
AND (child ¬ node.child) #
NIL
THEN
RETURN [Transform[node: child, wantFirst: TRUE], nodeLevel+1]; -- return the child
DO
-- move to next node,
sibling or up* then sibling
IF node.next#NIL THEN RETURN [Transform[node: node.next, wantFirst: TRUE], nodeLevel]; -- return the sibling
IF (node ¬ node.parent) = NIL THEN RETURN [NIL, 0]; -- go to the parent
nodeLevel ¬ nodeLevel-1;
ENDLOOP;
};
BackwardClipped:
PUBLIC
PROC [node: Node, maxLevel:
INTEGER,
parent: Node ¬
NIL, nodeLevel:
INTEGER ¬ 0]
RETURNS [back, backparent: Node, backLevel:
INTEGER] = {
like Backward, but limits how deep will go in tree
backLevel = Level[back] <= MAX[maxLevel, Level[node]]
child, child2: Node;
IF node=NIL OR (parent ¬ XParent[node])=NIL THEN RETURN [NIL, NIL, 0];
IF nodeLevel <= 0 THEN nodeLevel ¬ Level[node];
IF (child ¬ parent.child) = node THEN RETURN [Transform[node: parent, wantFirst: FALSE], Parent[parent], nodeLevel-1];
DO
-- search for sibling just before node
IF child.next=NIL THEN ERROR; -- incorrect value supplied for parent
IF (child2 ¬ child.next)=node THEN EXIT;
child ¬ child2;
ENDLOOP;
child ¬ Transform[node: child, wantFirst: FALSE];
DO
-- go deeper in tree until reach maxLevel
IF nodeLevel >= maxLevel OR (child2 ¬ LastChild[child]) = NIL THEN
RETURN [child, parent, nodeLevel];
nodeLevel ¬ nodeLevel+1;
parent ¬ child;
child ¬ child2;
ENDLOOP;
};
XLocRelative:
PROC [location: Location, count:
INT,
break:
NAT ¬ 1, skipCommentNodes:
BOOL ¬
FALSE]
RETURNS [Location] ~ {
rem: INT ¬ MAX[count, 0];
last: Location ¬ [NIL, 0];
FOR node: Node ← location.node, XForward[node].nx
UNTIL node=
NIL
DO
IF
NOT (skipCommentNodes
AND node.comment)
THEN {
size: INT ~ Size[node];
start: INT ~ IF node=location.node THEN MIN[MAX[location.where, 0], size] ELSE 0;
len: INT ~ size-start;
IF rem<=len THEN RETURN[[node, start+rem]];
rem ¬ rem-MIN[len+break, rem];
last ¬ [node, size];
};
ENDLOOP;
RETURN[last];
};
LocRelative:
PUBLIC
PROC [location: Location, count:
INT,
break:
NAT ¬ 1, skipCommentNodes:
BOOL ¬
FALSE]
RETURNS [Location] ~ {
loc: Location ~ XLocRelative[location, count, break, skipCommentNodes];
RETURN[[Transform[node: loc.node, wantFirst: TRUE], loc.where]];
};
LocWithin:
PUBLIC
PROC [n: Node, count:
INT,
break:
NAT ¬ 1, skipCommentNodes:
BOOL ¬
FALSE]
RETURNS [Location] ~ {
RETURN[LocRelative[[n, 0], count, break, skipCommentNodes]];
};
BadArgs:
PUBLIC
ERROR ~
CODE;
LocOffset:
PUBLIC
PROC [loc1, loc2: Location,
break:
NAT ¬ 1, skipCommentNodes:
BOOL ¬
FALSE]
RETURNS [count:
INT ¬ 0] ~ {
FOR node: Node ← loc1.node, XForward[node].nx
UNTIL node=
NIL
DO
IF
NOT (skipCommentNodes
AND node.comment)
THEN {
size: INT ~ Size[node];
i0: INT ~ IF node=loc1.node THEN MIN[MAX[loc1.where, 0], size] ELSE 0;
i1: INT ~ IF node=loc2.node THEN MIN[MAX[loc2.where, 0], size] ELSE size+break;
count ¬ count+(i1-i0);
};
IF node=loc2.node THEN RETURN;
ENDLOOP;
IF loc2.node#NIL THEN ERROR BadArgs;
};
LocNumber:
PUBLIC
PROC [at: Location,
break:
NAT ¬ 1, skipCommentNodes:
BOOL ¬
FALSE]
RETURNS [count:
INT] ~ {
RETURN[LocOffset[[Root[at.node], 0], at, break, skipCommentNodes]];
};
FirstSibling:
PUBLIC
PROC [n: Node]
RETURNS [Node] = {
RETURN[FirstChild[Parent[n]]];
};
LastSibling:
PUBLIC
PROC [n: Node]
RETURNS [Node] = {
IF n=NIL THEN RETURN [NIL];
UNTIL n.next=NIL DO n ¬ n.next ENDLOOP;
RETURN[Transform[node: n, wantFirst: FALSE]];
};
LastChild:
PUBLIC
PROC [n: Node]
RETURNS [Node] = {
RETURN[LastSibling[FirstChild[n]]];
};
LastWithin:
PUBLIC
PROC [n: Node]
RETURNS [Node] = {
nxt: Node;
IF n=NIL THEN RETURN [NIL];
IF (nxt ¬ n.child)=NIL THEN RETURN [n];
n ¬ nxt;
DO
-- keep going to child of last sibling
IF n.next=
NIL
THEN {
IF (nxt ¬ n.child)=NIL THEN RETURN [Transform[node: n, wantFirst: FALSE]];
n ¬ nxt
}
ELSE n ¬ n.next;
ENDLOOP;
};
LastLocWithin:
PUBLIC
PROC [n: Node]
RETURNS [Location] = {
last: Node ¬ LastWithin[n];
where: INT ¬ IF last # NIL THEN Rope.Size[last.rope] ELSE Tioga.NodeItself;
RETURN [[last, where]];
};
NthChild:
PUBLIC
PROC [n: Node, location:
INT ¬ 0]
RETURNS [child: Node] = {
note: NthChild[n, 0]==FirstChild[n]; NthChild[n, k+1]==Next[NthChild[n, k]]
IF n=NIL OR (child ¬ n.child)=NIL THEN RETURN;
DO
IF location=0
THEN
RETURN [Transform[node: child, wantFirst:
TRUE]];
child ¬ Transform[node: child, wantFirst: TRUE];
IF child.next=NIL THEN RETURN [NIL];
child ¬ child.next;
location ¬ location-1;
ENDLOOP;
};
NthSibling:
PUBLIC
PROC [n: Node, cnt:
INT ¬ 0]
RETURNS [Node] = {
note: NthSibling[n, 0]==n; NthSibling[n, k+1]==Next[NthSibling[n, k]]
IF n=NIL THEN RETURN [NIL];
DO
IF cnt=0
THEN
RETURN [Transform[node: n, wantFirst:
TRUE]];
n ¬ Transform[node: n, wantFirst: TRUE];
IF n.next=NIL THEN RETURN [NIL];
n ¬ n.next;
cnt ¬ cnt-1;
ENDLOOP;
};
CountChildren:
PUBLIC
PROC [n: Node]
RETURNS [count:
INT ¬ 0] = {
child: Node;
IF (child ¬ FirstChild[n])=NIL THEN RETURN;
DO count ¬ count+1;
child ¬ Transform[node: child, wantFirst: TRUE];
IF child.next=NIL THEN RETURN;
child ¬ child.next;
ENDLOOP;
};
CountFollowing:
PUBLIC
PROC [n: Node]
RETURNS [count:
INT ¬ 0] = {
IF n=NIL THEN RETURN;
UNTIL n.next=
NIL
DO
n ¬ n.next;
count ¬ count+1;
n ¬ Transform[node: n, wantFirst: TRUE];
ENDLOOP;
};
CountToParent:
PUBLIC
PROC [n: Node]
RETURNS [count:
INT ¬ 0, parent: Node] = {
IF n=NIL THEN RETURN;
UNTIL n.next=
NIL
DO
n ¬ n.next;
count ¬ count+1;
n ¬ Transform[node: n, wantFirst: TRUE];
ENDLOOP;
parent ¬ n.next;
parent ¬ Transform[node: parent, wantFirst: FALSE];
};
CountToChild:
PUBLIC
PROC [parent, child: Node]
RETURNS [count:
INT ¬ 0] = {
note: CountToChild[parent, FirstChild[parent]]==0
n: Node;
IF parent=NIL OR child=NIL THEN RETURN;
n ¬ Transform[node: parent.child, wantFirst: TRUE];
DO
SELECT n
FROM
child => RETURN [count];
NIL => RETURN [TextNode.MaxLen];
ENDCASE;
n ¬ Next[n];
count ¬ count+1;
ENDLOOP;
};
EndPos:
PUBLIC
PROC [n: Node]
RETURNS [
INT] = {
IF n=NIL THEN RETURN [0];
RETURN [MAX[Rope.Size[n.rope], 1]-1];
};
DestroyTree:
PUBLIC
PROC [root: Node] ~ {
parent: Node ~ Parent[root];
next: Node ¬ root;
UNTIL next=parent
DO
-- go through the tree zapping REF's
node: Node ~ next;
IF node.child#NIL THEN { next ¬ node.child; node.child ¬ NIL }
ELSE {
next ¬ IF node.next#NIL THEN node.next ELSE node.parent;
node.deleted ¬ TRUE;
node.parent ¬ NIL;
node.next ¬ NIL;
node.rope ¬ NIL;
node.runs ¬ NIL;
node.charSets ¬ NIL;
node.charProps ¬ NIL;
node.nodeProps ¬ NIL;
};
ENDLOOP;
};