<> <> <> <> <> <> <> <<>> 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]; NodePropsImpl: CEDAR MONITOR IMPORTS Atom, Basics, IO, List, MessageWindow, RefTab, Rope, Rosary EXPORTS Tioga ~ 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[BOOL _ FALSE], TRUE: NEW[BOOL _ TRUE]]; 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: BOOL _ FALSE; 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 ~ { <> <)*, where is encoded with the variable-length integer encoding scheme described in FileOps, and 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: BOOLEAN _ FALSE] ~ { 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: BOOL _ FALSE] ~ { 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 ~ { <> <> <<(()*)*,>> <> 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: ROPE _ NIL] ~ { 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: ROPE _ NIL; 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: BOOLEAN _ FALSE] ~ { IF int > 127 THEN {PutInt[int/128, TRUE]; int _ int MOD 128}; IF more THEN int _ int + 128; PutByte[int]; }; rept: INT _ 0; itemSpecs: ROPE _ NIL; 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: BOOL _ FALSE] ~ { save: ROPE ~ Grab[]; SpecsList: PROC [propList: Atom.PropList] RETURNS [s: LIST OF REF _ NIL] ~ { 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: REF _ NIL] ~ { 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: BOOL _ TRUE] RETURNS [quit: BOOL] ~ { <> <> 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] }; <> 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] ~ { <> <> 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 ~ { <> <> <> 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 ~ { <> <> <> 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 ~ { <> <> <> 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]; }; <> attributeTable: LIST OF LIST OF ATOM _ NIL; 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]; }; <> <<$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>> <> 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.