NodePropsImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
written by Paxton. January 1981
Michael Plass, May 28, 1986 3:38:28 pm PDT
Rick Beach, March 28, 1985 9:57:47 am PST
Russ Atkinson (RRA) August 19, 1985 4:17:41 pm PDT
Doug Wyatt, September 22, 1986 2:45:47 pm PDT
DIRECTORY
Atom USING [DottedPairNode, GetPName, MakeAtom, PropList],
Basics USING [BoundsCheckHighHalf, DoubleShiftLeft, NonNegative],
IO USING [atom, int, PutFR],
List USING [Length, Sort],
MessageWindow USING [Append, Blink],
RefTab USING [Create, Fetch, Ref, Store],
Rope USING [Concat, Equal, Fetch, FromChar, FromRefText, IsEmpty, ROPE, Size, Substr],
Rosary USING [FromProcProc, MapRuns, ROSARY, Size],
Tioga USING [MapPropsAction, Node, PropCopier, PropReader, Props, PropsBody, PropWriter],
TiogaPrivate USING [];
NodePropsImpl: CEDAR MONITOR
IMPORTS Atom, Basics, IO, List, MessageWindow, RefTab, Rope, Rosary
EXPORTS Tioga, TiogaPrivate
~ BEGIN
ROSARY: TYPE = Rosary.ROSARY;
ROPE: TYPE = Rope.ROPE;
Node: TYPE = Tioga.Node;
Prop, Props: TYPE = Tioga.Props;
PropsBody: TYPE = Tioga.PropsBody;
MapPropsAction: TYPE = Tioga.MapPropsAction;
PropReader: TYPE = Tioga.PropReader;
PropWriter: TYPE = Tioga.PropWriter;
PropCopier: TYPE = Tioga.PropCopier;
commentAtom: ATOM = $Comment;
formatAtom: ATOM = $Format;
styleDefAtom: ATOM = $StyleDef;
prefixAtom: ATOM = $Prefix;
postfixAtom: ATOM = $Postfix;
charpropsAtom: ATOM = $CharProps;
charsetsAtom: ATOM = $CharSets;
artworkAtom: ATOM = $Artwork;
refBool: ARRAY BOOL OF REF BOOL ← [FALSE: NEW[BOOLFALSE], TRUE: NEW[BOOLTRUE]];
RefBool: PUBLIC PROC [b: BOOL] RETURNS [REF BOOL] ~ { RETURN[refBool[b]] };
ReadComment: PropReader ~ {
comment: BOOL ~ Rope.Equal[specs, "TRUE", FALSE];
RETURN [refBool[comment]];
};
WriteComment: PropWriter ~ {
comment: BOOLFALSE;
WITH value SELECT FROM
x: REF BOOL => comment ← x^;
ENDCASE => NULL;
RETURN [IF comment THEN "TRUE" ELSE "FALSE"];
};
ReadFormat: PropReader ~ {
RETURN[IF Rope.IsEmpty[specs] THEN NIL ELSE Atom.MakeAtom[specs]];
};
WriteFormat: PropWriter ~ {
IF value=NIL THEN RETURN[""];
WITH value SELECT FROM
atom: ATOM => RETURN[Atom.GetPName[atom]];
ENDCASE => RETURN[NIL];
};
ReadCharSets: PropReader ~ {
Raises a bounds fault if invalid specs are provided.
Encoding is (<repeatCount><charSet>)*, where <repeatCount> is encoded with the variable-length integer encoding scheme described in FileOps, and <charSet> is just encoded as a byte.
p: PROC [q: PROC [REF, INT]] ~ {
i: INT ← 0;
size: INT ← Rope.Size[specs];
GetByte: PROC RETURNS [b: [0..256)] ~ {b ← Rope.Fetch[specs, i]-'\000; i ← i + 1};
Combine: PROC [a: INT, b: [0..128)] RETURNS [INT] ~ {
bnd: NAT ~ LAST[NAT]/128+1;
RETURN [Basics.DoubleShiftLeft[[li[Basics.BoundsCheckHighHalf[a, bnd]]], 7].li + b];
};
GetInt: PROC RETURNS [int: INT ← 0] ~ {
b: [0..256) ← GetByte[];
WHILE b > 127 DO
int ← Combine[int, b-128];
b ← GetByte[];
ENDLOOP;
int ← Combine[int, b];
};
UNTIL i = size DO
repeat: INT ~ GetInt[];
charSet: [0..256) ~ GetByte[];
IF charSet=0 THEN q[NIL, repeat] ELSE q[NEW[[0..256) ← charSet], repeat];
ENDLOOP;
};
RETURN [Rosary.FromProcProc[p]];
};
WriteCharSets: PropWriter ~ {
text: REF TEXT ~ NEW[TEXT[48]];
Fold: PROC ~ {specs ← Rope.Concat[specs, Rope.FromRefText[text]]; text.length ← 0};
PutByte: PROC [b: [0..256)] ~ {
IF text.length = text.maxLength THEN Fold[];
text[text.length] ← VAL[b];
text.length ← text.length + 1;
};
PutInt: PROC [int: INT, more: BOOLEANFALSE] ~ {
IF int > 127 THEN {PutInt[int/128, TRUE]; int ← int MOD 128};
IF more THEN int ← int + 128;
PutByte[int];
};
charSet: [0..256) ← 0;
charSetRepeat: INT ← 0;
FlushRun: PROC ~ {
IF charSetRepeat > 0 THEN {PutInt[charSetRepeat]; PutByte[charSet]; charSetRepeat ← 0};
};
Action: PROC [item: REF, repeat: INT] RETURNS [quit: BOOLFALSE] ~ {
new: [0..256) ← WITH item SELECT FROM
r: REF [0..256) => r^,
ENDCASE => 0;
IF charSetRepeat > 0 AND new#charSet THEN FlushRun[];
charSet ← new;
charSetRepeat ← charSetRepeat + repeat;
};
specs ← NIL;
[] ← Rosary.MapRuns[[NARROW[value]], Action];
FlushRun[];
Fold[];
IF specs#NIL AND Rope.Size[specs]=0 THEN specs ← NIL;
};
ReadCharProps: PropReader ~ {
Raises a bounds fault if invalid specs are provided.
Encoding is
(<repeatCount><propsCount>(<atomLen><atomBytes><valueLen><valueBytes>)*)*,
where the counts and lengths are encoded with the variable-length integer encoding scheme described in FileOps.
p: PROC [q: PROC [REF, INT]] ~ {
i: INT ← 0;
size: INT ← Rope.Size[specs];
GetByte: PROC RETURNS [b: [0..256)] ~ {b ← Rope.Fetch[specs, i]-'\000; i ← i + 1};
Combine: PROC [a: INT, b: [0..128)] RETURNS [INT] ~ {
bnd: NAT ~ LAST[NAT]/128+1;
RETURN [Basics.DoubleShiftLeft[[li[Basics.BoundsCheckHighHalf[a, bnd]]], 7].li + b];
};
GetInt: PROC RETURNS [int: INT ← 0] ~ {
b: [0..256) ← GetByte[];
WHILE b > 127 DO
int ← Combine[int, b-128];
b ← GetByte[];
ENDLOOP;
int ← Combine[int, b];
};
GetRope: PROC RETURNS [rope: ROPENIL] ~ {
len: INT ~ GetInt[];
rope ← Rope.Substr[specs, i, len];
i ← i + len;
[] ← Basics.NonNegative[size-i];
};
UNTIL i = size DO
repeatCount: INT ~ GetInt[];
propsCount: INT ~ GetInt[];
propList: Atom.PropList ← NIL;
THROUGH [0..propsCount) DO
atom: ATOM ~ Atom.MakeAtom[GetRope[]];
valueSpecs: ROPE ~ GetRope[];
value: REF ~ ReadProp[atom, valueSpecs];
propList ← CONS[NEW[Atom.DottedPairNode ← [key: atom, val: value]], propList];
ENDLOOP;
q[propList, repeatCount];
ENDLOOP;
};
RETURN [Rosary.FromProcProc[p]];
};
WriteCharProps: PropWriter ~ {
curRope: ROPENIL;
Grab: PROC RETURNS [rope: ROPE] ~ {rope ← curRope; curRope ← NIL};
PutByte: PROC [b: [0..256)] ~ {curRope ← Rope.Concat[curRope, Rope.FromChar[VAL[b]]]};
PutRope: PROC [rope: ROPE] ~ {curRope ← Rope.Concat[curRope, rope]};
PutInt: PROC [int: INT, more: BOOLEANFALSE] ~ {
IF int > 127 THEN {PutInt[int/128, TRUE]; int ← int MOD 128};
IF more THEN int ← int + 128;
PutByte[int];
};
rept: INT ← 0;
itemSpecs: ROPENIL;
FlushRun: PROC ~ { IF rept > 0 THEN {PutInt[rept]; PutRope[itemSpecs]; rept ← 0} };
PutItem: PROC [newItemSpecs: ROPE, repeat: INT] ~ {
IF NOT Rope.Equal[newItemSpecs, itemSpecs] THEN FlushRun[];
itemSpecs ← newItemSpecs;
rept ← rept + repeat;
};
Action: PROC [item: REF, repeat: INT] RETURNS [quit: BOOLFALSE] ~ {
save: ROPE ~ Grab[];
SpecsList: PROC [propList: Atom.PropList] RETURNS [s: LIST OF REFNIL] ~ {
FOR p: Atom.PropList ← propList, p.rest UNTIL p = NIL DO
atom: ATOM ~ NARROW[p.first.key];
v: ROPE ~ WriteProp[atom, p.first.val];
IF v # NIL THEN {
a: ROPE ~ Atom.GetPName[atom];
PutInt[Rope.Size[a]]; PutRope[a]; PutInt[Rope.Size[v]]; PutRope[v];
s ← CONS[Grab[], s];
};
ENDLOOP;
};
specList: LIST OF REF ← List.Sort[SpecsList[NARROW[item]]];
newItemSpecs: ROPE;
PutInt[List.Length[specList]];
UNTIL specList = NIL DO
t: LIST OF REF ← specList;
PutRope[NARROW[t.first]];
specList ← t.rest;
t.rest ← NIL;
ENDLOOP;
newItemSpecs ← Grab[];
curRope ← save;
PutItem[newItemSpecs, repeat];
};
[] ← Rosary.MapRuns[[NARROW[value]], Action];
FlushRun[];
specs ← Grab[];
IF specs#NIL AND Rope.Size[specs]=0 THEN specs ← NIL;
};
FindName: PROC [n: Node, name: ATOM, remove: BOOL] RETURNS [prop: Prop] ~ {
lst, prev: Props;
IF n=NIL THEN RETURN [NIL];
lst ← n.props;
prop ← NIL; prev ← NIL;
WHILE lst#NIL DO
IF lst.name = name THEN { prop ← lst; EXIT };
prev ← lst; lst ← lst.next;
ENDLOOP;
IF prop#NIL AND remove THEN {
IF prev#NIL THEN prev.next ← prop.next
ELSE n.props ← prop.next;
};
};
PutProp: PUBLIC PROC [n: Node, name: ATOM, value: REF] ~ {
prop: Prop ← FindName[n, name, FALSE];
IF name = charpropsAtom OR name = charsetsAtom THEN {
value ← CheckRosarySize[NARROW[value], n, name];
};
IF value = NIL THEN {
IF prop#NIL THEN prop.value ← NIL;
SELECT name FROM
styleDefAtom => n.hasstyledef ← FALSE;
prefixAtom => n.hasprefix ← FALSE;
postfixAtom => n.haspostfix ← FALSE;
formatAtom => n.formatName ← NIL;
commentAtom => n.comment ← FALSE;
charpropsAtom => n.hascharprops ← FALSE;
charsetsAtom => n.hascharsets ← FALSE;
artworkAtom => n.hasartwork ← FALSE;
ENDCASE;
RETURN
};
SELECT name FROM
styleDefAtom => n.hasstyledef ← TRUE;
prefixAtom => n.hasprefix ← TRUE;
postfixAtom => n.haspostfix ← TRUE;
formatAtom => {
n.formatName ← NARROW[value];
RETURN;
};
commentAtom => { -- simply set the bit in the node
n.comment ← NARROW[value, REF BOOL]^;
RETURN;
};
charpropsAtom => n.hascharprops ← TRUE;
charsetsAtom => n.hascharsets ← TRUE;
artworkAtom => n.hasartwork ← TRUE;
ENDCASE;
IF prop#NIL THEN prop.value ← value
ELSE n.props ← NEW[PropsBody ← [name: name, value: value, next: n.props]];
};
CheckRosarySize: PROC [rosary: ROSARY, n: Node, what: ATOM]
RETURNS [ROSARY] ~ {
IF rosary # NIL THEN {
rosarySize: INT ~ Rosary.Size[rosary];
ropeSize: INT ~ Rope.Size[n.rope];
IF rosarySize # ropeSize THEN {
msg: ROPE ~ IO.PutFR["NodePropsImpl: %g property discarded because its size (%g) differed from rope size (%g).", IO.atom[what], IO.int[rosarySize], IO.int[ropeSize]];
MessageWindow.Append[msg, TRUE];
MessageWindow.Blink[];
RETURN [NIL];
};
};
RETURN [rosary];
};
GetProp: PUBLIC PROC [node: Node, name: ATOM] RETURNS [value: REFNIL] ~ {
IF node#NIL THEN {
prop: Prop ~ FindName[node, name, FALSE];
IF prop#NIL THEN value ← prop.value
ELSE SELECT name FROM
commentAtom => value ← refBool[node.comment];
formatAtom => IF node.formatName#NIL THEN value ← node.formatName;
ENDCASE;
};
};
RemProp: PUBLIC PROC [n: Node, name: ATOM] ~ {
[] ← FindName[n, name, TRUE]; -- removes the value
SELECT name FROM
styleDefAtom => n.hasstyledef ← FALSE;
prefixAtom => n.hasprefix ← FALSE;
postfixAtom => n.haspostfix ← FALSE;
formatAtom => n.formatName ← NIL;
commentAtom => n.comment ← FALSE;
charpropsAtom => n.hascharprops ← FALSE;
charsetsAtom => n.hascharsets ← FALSE;
artworkAtom => n.hasartwork ← FALSE;
ENDCASE;
};
MapProps: PUBLIC PROC [node: Node, action: MapPropsAction,
formatFlag, commentFlag: BOOLTRUE] RETURNS [quit: BOOL] ~ {
apply the action to each name & value pair for the node
returns true if&when an action returns true
IF node#NIL THEN {
props: Props ← node.props;
IF formatFlag AND node.formatName#NIL AND action[formatAtom, node.formatName] THEN RETURN [TRUE];
IF commentFlag AND action[commentAtom, refBool[node.comment]] THEN RETURN [TRUE];
UNTIL props=NIL DO
next: Props ~ props.next; -- get it now in case action deletes current prop
IF props.value#NIL AND action[props.name, props.value] THEN RETURN [TRUE];
props ← next;
ENDLOOP;
};
RETURN [FALSE]
};
Read, Write, Copy Props
PropProcs: TYPE ~ REF PropProcsRep;
PropProcsRep: TYPE ~ RECORD [reader: PropReader, writer: PropWriter, copier: PropCopier];
propProcsTable: RefTab.Ref ← RefTab.Create[];
RegisterProp: PUBLIC PROC [name: ATOM,
reader: PropReader, writer: PropWriter, copier: PropCopier] ~ {
registers these procs for this variety of node
they will be called by DoSpecs, GetSpecs, and CopyInfo
IF name#NIL THEN {
procs: PropProcs ~ NEW[PropProcsRep ← [reader: reader, writer: writer, copier: copier]];
[] ← RefTab.Store[propProcsTable, name, procs];
};
};
NullRead: PUBLIC PropReader ~ { RETURN [NIL] };
NullWrite: PUBLIC PropWriter ~ { RETURN [NIL] };
NullCopy: PUBLIC PropCopier ~ { RETURN [NIL] };
ReadProp: PUBLIC PropReader ~ {
used when reading files
calls the registered reader for this property name
returns specs if no reader is registered
reader: PropReader ← NIL;
WITH RefTab.Fetch[propProcsTable, name].val SELECT FROM
procs: PropProcs => reader ← procs.reader;
ENDCASE;
IF reader#NIL THEN RETURN[reader[name, specs]]
ELSE RETURN[specs];
};
WriteProp: PUBLIC PropWriter ~ {
used when writing files
calls the registered writer for this property name
if no writer is registered, returns value if it is a rope, NIL otherwise
writer: PropWriter ← NIL;
WITH RefTab.Fetch[propProcsTable, name].val SELECT FROM
procs: PropProcs => writer ← procs.writer;
ENDCASE;
IF writer#NIL THEN RETURN[writer[name, value]]
ELSE WITH value SELECT FROM rope: ROPE => RETURN[rope]; ENDCASE => RETURN[NIL];
};
CopyProp: PUBLIC PropCopier ~ {
used when copying nodes
calls the registered copier for this property name
if no copier is registered, returns value
copier: PropCopier ← NIL;
WITH RefTab.Fetch[propProcsTable, name].val SELECT FROM
procs: PropProcs => copier ← procs.copier;
ENDCASE;
IF copier#NIL THEN RETURN[copier[name, value]]
ELSE RETURN[value];
};
Property Attributes
attributeTable: LIST OF LIST OF ATOMNIL;
DeclarePropertyAttribute: PUBLIC ENTRY PROC [name: ATOM, attribute: ATOM] ~ {
FOR a: LIST OF LIST OF ATOM ← attributeTable, a.rest UNTIL a=NIL DO
IF a.first.first = name THEN {
FOR p: LIST OF ATOM ← a.first.rest, p.rest UNTIL p=NIL DO
IF p.first = attribute THEN RETURN;
ENDLOOP;
a.first.rest ← CONS[attribute, a.first.rest];
RETURN;
};
ENDLOOP;
attributeTable ← CONS[LIST[name, attribute], attributeTable];
};
Is: PUBLIC ENTRY PROC [name: ATOM, attribute: ATOM] RETURNS [BOOL] ~ {
FOR a: LIST OF LIST OF ATOM ← attributeTable, a.rest UNTIL a=NIL DO
IF a.first.first = name THEN {
FOR p: LIST OF ATOM ← a.first.rest, p.rest UNTIL p=NIL DO
IF p.first = attribute THEN RETURN [TRUE];
ENDLOOP;
RETURN [FALSE];
};
ENDLOOP;
RETURN [FALSE];
};
GetPropertyAttributes: PUBLIC ENTRY PROC [name: ATOM] RETURNS [LIST OF ATOM] ~ {
FOR a: LIST OF LIST OF ATOM ← attributeTable, a.rest UNTIL a=NIL DO
IF a.first.first = name THEN RETURN [a.first.rest];
ENDLOOP;
RETURN [NIL];
};
SetPropertyAttributes: PUBLIC ENTRY PROC [name: ATOM, attributes: LIST OF ATOM] ~ {
FOR a: LIST OF LIST OF ATOM ← attributeTable, a.rest UNTIL a=NIL DO
IF a.first.first = name THEN {a.first.rest ← attributes; RETURN};
ENDLOOP;
attributeTable ← CONS[CONS[name, attributes], attributeTable];
};
Property Attribute Documentation
$Visible  says whether property affects appearance
$Inheritable says whether property may be inherited when a new node is inserted
$ClientOnly asserts EditTool interface should not change the value
Initialization
Init: PROC ~ {
visible: LIST OF ATOM ~ LIST[$Visible];
visAndInherit: LIST OF ATOM ~ CONS[$Inheritable, visible];
visAndClient: LIST OF ATOM ~ CONS[$ClientOnly, visible];
RegisterProp[formatAtom, ReadFormat, WriteFormat, NIL];
SetPropertyAttributes[formatAtom, visAndInherit];
RegisterProp[commentAtom, ReadComment, WriteComment, NIL];
SetPropertyAttributes[commentAtom, visAndInherit];
RegisterProp[$CharSets, ReadCharSets, WriteCharSets, NIL];
SetPropertyAttributes[$CharSets, visAndClient];
RegisterProp[$CharProps, ReadCharProps, WriteCharProps, NIL];
SetPropertyAttributes[$CharProps, visAndClient];
SetPropertyAttributes[prefixAtom, visAndInherit];
SetPropertyAttributes[postfixAtom, visAndInherit];
SetPropertyAttributes[$StyleDef, visAndInherit];
SetPropertyAttributes[$Artwork, visible];
SetPropertyAttributes[$Interpress, visible];
SetPropertyAttributes[$Bounds, visible];
};
Init[];
END.