TiogaAccessImpl.mesa
Copyright Ó 1985, 1986, 1988, 1989, 1990, 1991, 1992 by Xerox Corporation. All rights reserved.
Michael Plass, April 4, 1991 5:39 pm PST
Tim Diebert May 13, 1985 12:06:39 pm PDT
Russ Atkinson (RRA) July 2, 1985 11:08:52 am PDT
Willie-s, April 5, 1991 2:48 pm PST
Doug Wyatt, July 9, 1992 5:12 pm PDT
DIRECTORY
Char USING [Code, Set],
IO USING [PutChar, RopeFromROS, ROS, STREAM],
NodeProps USING [PutProp, DoSpecs, GetSpecs],
NodeReader,
PFS,
Prop USING [PropList],
Rope USING [Concat, FromRefText, Flatten, Length, ROPE, Substr],
Rosary USING [Concat, FromItem, ROSARY, Substr],
TextEdit,
TextNode,
Tioga USING [Looks, noLooks, PropList, Node, Location],
TiogaAccess USING [TiogaChar],
TiogaAccessPrivate USING [Reader, ReaderRep, ropePieces, Writer, WriterRep],
TiogaIO USING [FromFile, FromOpenFile, ToFile, ToOpenFile];
TiogaAccessImpl: CEDAR PROGRAM
IMPORTS Char, IO, NodeProps, NodeReader, PFS, Rope, Rosary, TextEdit, TextNode, TiogaIO
EXPORTS TiogaAccess
~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
ROSARY: TYPE ~ Rosary.ROSARY;
TiogaChar: TYPE ~ TiogaAccess.TiogaChar;
Node: TYPE ~ Tioga.Node;
Location: TYPE ~ Tioga.Location;
Looks: TYPE ~ Tioga.Looks;
noLooks: Looks ~ Tioga.noLooks;
PropList: TYPE ~ Tioga.PropList;
GetExternalProp: PUBLIC PROC [key: ATOM, value: REF] RETURNS [ROPE] ~ {
RETURN [NodeProps.GetSpecs[key, value]]
};
GetInternalProp: PUBLIC PROC [key: ATOM, value: ROPE] RETURNS [REF] ~ {
RETURN [NodeProps.DoSpecs[key, value]]
};
Error: PUBLIC ERROR [group: PFS.ErrorGroup, expl: ROPE] ~ CODE;
Reader: TYPE ~ TiogaAccessPrivate.Reader;
ReaderRep: PUBLIC TYPE ~ TiogaAccessPrivate.ReaderRep;
GetLocation: PUBLIC PROC [reader: Reader] RETURNS [Location] ~ {
RETURN[[reader.node, reader.index]];
};
LocNumber: PROC [reader: Reader, skip: BOOL ¬ FALSE] RETURNS [INT] ~ {
RETURN[TextNode.LocOffset[loc1: [reader.root, 0], loc2: [reader.node, reader.index],
skipCommentNodes: skip]];
};
LocWithin: PROC [reader: Reader, count: INT, skip: BOOL ¬ FALSE] RETURNS [Location] ~ {
RETURN[TextNode.LocRelative[location: [reader.root, 0], count: count,
skipCommentNodes: skip]];
};
SetLocation: PROC [reader: Reader, location: Location] ~ {
prevNode: Node ~ reader.node;
IF reader.putback#NIL THEN DiscardPutBack[reader];
NodeReader.Set[reader.rdr, reader.node ¬ location.node];
reader.index ¬ MIN[MAX[location.where, 0], NodeReader.Size[reader.rdr]];
IF reader.node#prevNode THEN reader.start ¬ LocNumber[reader]-reader.index;
};
SkipToNextNode: PUBLIC PROC [reader: Reader] RETURNS [deltaLevel: INT] ~ {
prevNode: Node ~ reader.node;
prevSize: INT ~ NodeReader.Size[reader.rdr];
IF reader.putback#NIL THEN DiscardPutBack[reader];
IF prevNode=NIL THEN Error[client, "Attempt to read past end of document"];
[reader.node, deltaLevel] ¬ TextNode.Forward[prevNode];
NodeReader.Set[reader.rdr, reader.node];
reader.index ¬ 0;
reader.start ¬ reader.start+prevSize+1;
};
GetLen: PROC [reader: Reader, skip: BOOL] RETURNS [INT] ~ {
loc1: Location ~ [reader.root, 0];
loc2: Location ~ TextNode.LastLocWithin[reader.root];
break: INT ~ IF (skip AND loc2.node.comment) THEN 0 ELSE 1;
RETURN[TextNode.LocOffset[loc1: loc1, loc2: loc2, skipCommentNodes: skip]+break];
};
GetLength: PUBLIC PROC [reader: Reader] RETURNS [INT] ~ {
loc1: Location ~ [reader.root, 0];
loc2: Location ~ TextNode.LastLocWithin[reader.root];
RETURN[TextNode.LocOffset[loc1: loc1, loc2: loc2, skipCommentNodes: FALSE]+1];
};
GetIndex: PUBLIC PROC [reader: Reader] RETURNS [index: INT] ~ {
RETURN[reader.start+reader.index];
};
SetIndex: PUBLIC PROC [reader: Reader, index: INT] ~ {
IF reader.putback#NIL THEN DiscardPutBack[reader];
IF index IN [reader.start..(reader.start+NodeReader.Size[reader.rdr])]
THEN reader.index ¬ index-reader.start
ELSE SetLocation[reader, LocWithin[reader, index]];
};
GetPosition: PUBLIC PROC [reader: Reader] RETURNS [INT] ~ {
RETURN[reader.offset+LocNumber[reader, TRUE]];
};
SetPosition: PUBLIC PROC [reader: Reader, position: INT] ~ {
SetLocation[reader, LocWithin[reader, position-reader.offset, TRUE]];
};
FromNode: PUBLIC PROC [node: Node, offset: INT ¬ 0] RETURNS [reader: Reader] ~ {
reader ¬ NEW[ReaderRep ¬ [root: TextNode.Root[node], rdr: NodeReader.New[]]];
SetLocation[reader, [node, 0]];
reader.offset ¬ offset-LocNumber[reader, TRUE];
};
FromFile: PUBLIC PROC [fileName: ROPE] RETURNS [Reader] ~ {
RETURN [FromNode[TiogaIO.FromFile[PFS.PathFromRope[fileName]].root]]
};
FromOpenFile: PUBLIC PROC [openFile: PFS.OpenFile] RETURNS [Reader] ~ {
RETURN [FromNode[TiogaIO.FromOpenFile[openFile].root]]
};
Get: PUBLIC PROC [reader: Reader] RETURNS [TiogaChar] ~ {
node: Node ~ reader.node;
index: INT ~ reader.index;
IF reader.putback#NIL THEN {
t: LIST OF TiogaChar ~ reader.putback;
reader.putback ¬ t.rest;
RETURN[t.first];
};
IF node=NIL THEN ERROR Error[client, "Attempt to read past end of document"];
IF index<NodeReader.Size[reader.rdr]
THEN {
info: NodeReader.CharInfo ~ NodeReader.Fetch[reader.rdr, index];
reader.index ¬ reader.index+1;
RETURN[[
charSet: Char.Set[info.char],
char: VAL[Char.Code[info.char]],
looks: info.looks,
propList: info.props,
format: node.format,
comment: node.comment,
deltaLevel: 0,
endOfNode: FALSE
]];
}
ELSE {
deltaLevel: INTEGER ~ SkipToNextNode[reader];
RETURN[[
charSet: 0,
char: '\n--reader.newline--,
looks: noLooks,
propList: node.nodeProps,
format: node.format,
comment: node.comment,
deltaLevel: deltaLevel,
endOfNode: TRUE
]];
};
};
Peek: PUBLIC PROC [reader: Reader] RETURNS [TiogaChar] ~ {
node: Node ~ reader.node;
index: INT ~ reader.index;
IF reader.putback#NIL THEN RETURN[reader.putback.first];
IF node=NIL THEN ERROR Error[client, "Attempt to read past end of document"];
IF index<NodeReader.Size[reader.rdr]
THEN {
info: NodeReader.CharInfo ~ NodeReader.Fetch[reader.rdr, index];
RETURN[[
charSet: Char.Set[info.char],
char: VAL[Char.Code[info.char]],
looks: info.looks,
propList: info.props,
format: node.format,
comment: node.comment,
deltaLevel: 0,
endOfNode: FALSE
]];
}
ELSE {
deltaLevel: INTEGER ~ TextNode.Forward[node].levelDelta;
RETURN[[
charSet: 0,
char: '\n--reader.newline--,
looks: noLooks,
propList: node.nodeProps,
format: node.format,
comment: node.comment,
deltaLevel: deltaLevel,
endOfNode: TRUE
]];
};
};
PeekRope: PUBLIC PROC [reader: Reader] RETURNS [rope: ROPE ¬ NIL] ~ {
IF reader.putback#NIL THEN {
Putback stuff is harder than all the rest!
s: IO.STREAM ~ IO.ROS[];
FOR t: LIST OF TiogaChar ¬ reader.putback, t.rest UNTIL t=NIL DO
IF t.first.endOfNode THEN RETURN[IO.RopeFromROS[s]];
IO.PutChar[s, t.first.char]; -- should we complain if charSet#0?
ENDLOOP;
rope ¬ IO.RopeFromROS[s];
};
IF reader.index<NodeReader.Size[reader.rdr] THEN rope ¬ Rope.Concat[rope,
Rope.Substr[base: reader.node.rope, start: reader.index]];
};
PeekRuns: PROC [reader: Reader] RETURNS [ROSARY] ~ {
node: Node ~ reader.node;
IF node.runs=NIL THEN RETURN[NIL];
RETURN[Rosary.Substr[base: node.runs, start: reader.index]];
};
PeekCharSets: PROC [reader: Reader] RETURNS [ROSARY] ~ {
node: Node ~ reader.node;
IF node.charSets=NIL THEN RETURN[NIL];
RETURN[Rosary.Substr[base: node.charSets, start: reader.index]];
};
PeekCharProps: PROC [reader: Reader] RETURNS [ROSARY] ~ {
node: Node ~ reader.node;
IF node.charProps=NIL THEN RETURN[NIL];
RETURN[Rosary.Substr[base: node.charProps, start: reader.index]];
};
EndOf: PUBLIC PROC [reader: Reader] RETURNS [BOOLEAN] ~ {
RETURN [reader.putback=NIL AND reader.node=NIL];
};
GetNodeProps: PUBLIC PROC [reader: Reader] RETURNS [PropList] ~ {
node: Node ~ reader.node;
RETURN [IF node=NIL THEN NIL ELSE node.nodeProps]
};
PutBack: PUBLIC PROC [reader: Reader, tiogaChar: TiogaChar] ~ {
reader.putback ¬ CONS[tiogaChar, reader.putback];
};
DiscardPutBack: PROC [reader: Reader] ~ {
UNTIL reader.putback = NIL DO
t: LIST OF TiogaChar ¬ reader.putback.rest;
reader.putback.rest ¬ NIL;
reader.putback ¬ t;
ENDLOOP;
};
DoneWith: PUBLIC PROC [reader: Reader] ~ {
IF reader.rdr#NIL THEN {
NodeReader.Free[reader.rdr];
reader.rdr ¬ NIL;
DiscardPutBack[reader];
};
};
Writer: TYPE ~ TiogaAccessPrivate.Writer;
WriterRep: PUBLIC TYPE ~ TiogaAccessPrivate.WriterRep;
ropePieces: NAT ~ TiogaAccessPrivate.ropePieces;
textBufSize: NAT ¬ (574-SIZE[TEXT[0]])*2;
Largest size that will come from the small-grain allocator (see Allocator.maxSmallBlockSize)
Create: PUBLIC PROC RETURNS [Writer] ~ {
new: Writer ¬ NEW[WriterRep];
new.textBuf ¬ NEW[TEXT[textBufSize]];
Reset[new];
RETURN [new];
};
InsertRoot: PROC [writer: Writer, x: Node ¬ NIL] ~ {
IF x = NIL THEN {
x ¬ TextNode.NewTextNode[];
NodeProps.PutProp[x, $NewlineDelimiter, Rope.Flatten["\n"]];
x.comment ¬ TRUE;
};
IF Rope.Length[x.rope] # 0 THEN Error[client, "Root may not contain text"];
x.parent ¬ x.next ¬ NIL;
writer.root ¬ x;
writer.last ¬ x;
writer.level ¬ writer.lastLevel ¬ 0;
};
CreateOneMoreLevel: PROC [writer: Writer] ~ {
IF writer.root = NIL THEN {
InsertRoot[writer, TextNode.NewTextNode[]];
}
ELSE {
new: TextNode.Ref ← TextNode.NewTextNode[];
new^ ← writer.root^;
writer.root.props ← NIL;
writer.root.hasstyledef ← writer.root.hasprefix ← writer.root.haspostfix ← writer.root.hascharprops ← writer.root.hascharsets ← writer.root.hasartwork ← FALSE;
new.last ← TRUE;
new.child ← writer.root;
new.next ← NIL;
writer.root.next ← new;
writer.root ← new;
writer.lastLevel ← writer.lastLevel + 1;
writer.level ← writer.level + 1;
};
};
InsertSibling: PROC [writer: Writer, new: Tioga.Node] ~ {
x: Tioga.Node ~ writer.last;
new.next ¬ x.next;
new.parent ¬ x.parent;
x.next ¬ new;
writer.last ¬ new;
};
InsertChild: PROC [writer: Writer, new: Tioga.Node] ~ {
x: Tioga.Node ~ writer.last;
IF x.child # NIL THEN ERROR;
new.parent ¬ x;
x.child ¬ new;
writer.last ¬ new;
writer.lastLevel ¬ writer.lastLevel + 1;
};
FoldText: PROC [writer: Writer] ~ {
FoldRope[writer, Rope.FromRefText[writer.textBuf]];
writer.textBuf.length ¬ 0;
};
FoldRope: PROC [writer: Writer, rope: ROPE] ~ {
i: NAT ¬ 0;
writer.textBuf.length ¬ 0;
DO
IF writer.ropes[i] = NIL THEN {writer.ropes[i] ¬ rope; EXIT};
rope ¬ Rope.Concat[writer.ropes[i], rope];
writer.ropes[i] ¬ NIL;
IF i < ropePieces-1 THEN i ¬ i + 1;
ENDLOOP;
};
GetRope: PROC [writer: Writer] RETURNS [rope: ROPE ¬ NIL] ~ {
FoldText[writer];
FOR i: NAT IN [0..ropePieces) DO
IF writer.ropes[i] # NIL THEN {
rope ¬ Rope.Concat[writer.ropes[i], rope];
writer.ropes[i] ¬ NIL;
};
ENDLOOP;
};
FoldRosary: PROC [rosary: ROSARY, item: REF, repeat, size: INT] RETURNS [ROSARY] ~ {
IF repeat=0 THEN RETURN[rosary];
IF rosary=NIL AND item=NIL THEN RETURN[NIL];
IF rosary=NIL THEN rosary ¬ Rosary.FromItem[NIL, size-repeat];
RETURN[Rosary.Concat[rosary, Rosary.FromItem[item, repeat]]];
};
ConcatRosary: PROC [r1: ROSARY, size1: INT, r2: ROSARY, size2: INT] RETURNS [ROSARY] ~ {
IF r1=NIL AND r2=NIL THEN RETURN[NIL];
IF r1=NIL THEN r1 ¬ Rosary.FromItem[NIL, size1];
IF r2=NIL THEN r2 ¬ Rosary.FromItem[NIL, size2];
RETURN[Rosary.Concat[r1, r2]];
};
FoldRuns: PROC [writer: Writer] ~ {
IF writer.newLooksRepeat>0 THEN writer.runs ¬ FoldRosary[rosary: writer.runs,
item: TextEdit.ItemFromLooks[writer.newLooks], repeat: writer.newLooksRepeat,
size: writer.nodeSize];
writer.newLooks ¬ noLooks;
writer.newLooksRepeat ¬ 0;
};
GetRuns: PROC [writer: Writer] RETURNS [runs: ROSARY] ~ {
FoldRuns[writer];
runs ¬ writer.runs;
writer.runs ¬ NIL;
};
FoldCharSets: PROC [writer: Writer] ~ {
IF writer.newCharSetRepeat>0 THEN writer.charSets ¬ FoldRosary[rosary: writer.charSets,
item: TextEdit.ItemFromCharSet[writer.newCharSet], repeat: writer.newCharSetRepeat,
size: writer.nodeSize];
writer.newCharSet ¬ 0;
writer.newCharSetRepeat ¬ 0;
};
GetCharSets: PROC [writer: Writer] RETURNS [charSets: ROSARY] ~ {
FoldCharSets[writer];
charSets ¬ writer.charSets;
writer.charSets ¬ NIL;
};
FoldCharProps: PROC [writer: Writer] ~ {
IF writer.newCharPropRepeat>0 THEN writer.charProps ¬ FoldRosary[rosary: writer.charProps,
item: writer.newCharProp, repeat: writer.newCharPropRepeat,
size: writer.nodeSize];
writer.newCharProp ¬ NIL;
writer.newCharPropRepeat ¬ 0;
};
GetCharProps: PROC [writer: Writer] RETURNS [charProps: ROSARY] ~ {
FoldCharProps[writer];
charProps ¬ writer.charProps;
writer.charProps ¬ NIL;
};
Put: PUBLIC PROC [writer: Writer, tiogaChar: TiogaChar] ~ {
IF tiogaChar.endOfNode THEN {
new: Node ¬ TextNode.NewTextNode[];
new.format ¬ tiogaChar.format;
new.comment ¬ tiogaChar.comment;
new.rope ¬ GetRope[writer];
new.runs ¬ GetRuns[writer];
new.charSets ¬ GetCharSets[writer];
new.charProps ¬ GetCharProps[writer];
FOR l: Prop.PropList ¬ tiogaChar.propList, l.rest UNTIL l = NIL DO
NodeProps.PutProp[new, NARROW[l.first.key], l.first.val];
ENDLOOP;
IF writer.root = NIL AND writer.level=0 AND tiogaChar.deltaLevel=1 AND Rope.Length[new.rope] = 0 THEN InsertRoot[writer, new]
ELSE {
IF writer.level < 1 THEN {
InsertRoot[writer];
writer.level ¬ 1;
};
WHILE writer.level < writer.lastLevel DO
writer.last ¬ TextNode.Parent[writer.last];
writer.lastLevel ¬ writer.lastLevel - 1;
ENDLOOP;
IF writer.level = writer.lastLevel THEN InsertSibling[writer, new]
ELSE IF writer.level = writer.lastLevel + 1 THEN InsertChild[writer, new]
ELSE Error[client, "Nesting level may not increase by more than one"];
IF writer.first = NIL THEN writer.first ¬ new;
};
writer.nodeSize ¬ 0;
IF writer.lastLevel # writer.level THEN ERROR;
writer.level ¬ MAX[writer.lastLevel + tiogaChar.deltaLevel, 1];
}
ELSE {
text: REF TEXT ~ writer.textBuf;
IF tiogaChar.looks # writer.newLooks THEN {
FoldRuns[writer];
writer.newLooks ¬ tiogaChar.looks;
};
writer.newLooksRepeat ¬ writer.newLooksRepeat + 1;
IF tiogaChar.charSet # writer.newCharSet THEN {
FoldCharSets[writer];
writer.newCharSet ¬ tiogaChar.charSet;
};
writer.newCharSetRepeat ¬ writer.newCharSetRepeat + 1;
IF tiogaChar.propList # writer.newCharProp THEN {
FoldCharProps[writer];
writer.newCharProp ¬ tiogaChar.propList;
};
writer.newCharPropRepeat ¬ writer.newCharPropRepeat + 1;
IF text.length = text.maxLength THEN FoldText[writer];
text[(text.length ¬ text.length+1)-1] ¬ tiogaChar.char;
writer.nodeSize ¬ writer.nodeSize + 1;
};
};
CopyNode: PUBLIC PROC [writer: Writer, reader: Reader, maxLength: INT] RETURNS [nodeEnd: BOOLEAN] ~ {
textRope: ROPE ¬ NIL;
textRuns: ROSARY ¬ NIL;
textCharSets: ROSARY ¬ NIL;
textCharProps: ROSARY ¬ NIL;
textSize: INT ¬ 0;
WHILE maxLength > 0 AND reader.putback # NIL DO
tchar: TiogaChar ¬ Get[reader];
Put[writer, tchar];
maxLength ¬ maxLength - 1;
IF tchar.endOfNode THEN RETURN [TRUE];
ENDLOOP;
FoldText[writer];
FoldRuns[writer];
FoldCharSets[writer];
FoldCharProps[writer];
textRope ¬ PeekRope[reader];
textSize ¬ Rope.Length[textRope];
textRuns ¬ PeekRuns[reader];
textCharSets ¬ PeekCharSets[reader];
textCharProps ¬ PeekCharProps[reader];
nodeEnd ¬ textSize < maxLength;
IF NOT nodeEnd THEN {
textRope ¬ Rope.Substr[textRope, 0, maxLength];
textSize ¬ Rope.Length[textRope];
IF textRuns # NIL THEN textRuns ¬ Rosary.Substr[textRuns, 0, textSize];
IF textCharSets # NIL THEN textCharSets ¬ Rosary.Substr[textCharSets, 0, textSize];
IF textCharProps # NIL THEN textCharSets ¬ Rosary.Substr[textCharProps, 0, textSize];
};
FoldRope[writer, textRope];
writer.runs ¬ ConcatRosary[writer.runs, writer.nodeSize, textRuns, textSize];
writer.charSets ¬ ConcatRosary[writer.charSets, writer.nodeSize, textCharSets, textSize];
writer.charProps ¬ ConcatRosary[writer.charProps, writer.nodeSize, textCharProps, textSize];
reader.index ¬ reader.index + textSize;
writer.nodeSize ¬ writer.nodeSize + textSize;
IF nodeEnd THEN Put[writer, Get[reader]];
};
Nest: PUBLIC PROC [writer: Writer, delta: INT] ~ {
level: INT ~ writer.level + delta;
IF level > writer.lastLevel+1 THEN Error[client, "Nesting level may not increase by more than one"];
writer.level ¬ level;
};
FinishWrite: PUBLIC PROC [writer: Writer, action: PROC [root, first, last: Node]] ~ {
Collects all the pieces together.
IF writer.nodeSize>0 THEN Put[writer, [
charSet: 0,
char: '\n,
looks: ALL[FALSE],
format: NIL,
comment: FALSE,
endOfNode: TRUE,
deltaLevel: 0,
propList: NIL
]];
action[writer.root, writer.first, writer.last];
Reset[writer];
};
WriteFile: PUBLIC PROC [writer: Writer, fileName: ROPE] ~ {
path: PFS.PATH ~ PFS.PathFromRope[fileName];
action: PROC [root, first, last: Node] ~ { [] ¬ TiogaIO.ToFile[path, root] };
FinishWrite[writer, action];
};
WriteOpenFile: PUBLIC PROC [writer: Writer, openFile: PFS.OpenFile] ~ {
action: PROC [root, first, last: Node] ~ { [] ¬ TiogaIO.ToOpenFile[openFile, root] };
FinishWrite[writer, action];
};
WriteReader: PUBLIC PROC [writer: Writer] RETURNS [reader: Reader] ~ {
action: PROC [root, first, last: Node] ~ { reader ¬ FromNode[root] };
FinishWrite[writer, action];
};
WriteNode: PUBLIC PROC [writer: Writer] RETURNS [node: Node] ~ {
action: PROC [root, first, last: Node] ~ { node ¬ root; writer.root ¬ NIL };
FinishWrite[writer, action];
};
Reset: PUBLIC PROC [writer: Writer] ~ {
IF writer.root # NIL THEN {
TEditInput.FreeTree[writer.root];
writer.root ¬ NIL;
};
writer.first ¬ writer.last ¬ NIL;
writer.level ¬ 0;
writer.lastLevel ¬ 0;
writer.nodeSize ¬ 0;
writer.ropes ¬ ALL[NIL];
writer.textBuf.length ¬ 0;
writer.runs ¬ NIL;
writer.newLooks ¬ noLooks;
writer.newLooksRepeat ¬ 0;
writer.charSets ¬ NIL;
writer.newCharSet ¬ 0;
writer.newCharSetRepeat ¬ 0;
writer.charProps ¬ NIL;
writer.newCharProp ¬ NIL;
writer.newCharPropRepeat ¬ 0;
};
END.