<<>> <> <> <> <> <> <> <> <<>> 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> 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> 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; }; <> <> <> <<}>> <> <> <> <> <> <> <> <> <> <> <> <> <<};>> <<};>> <<>> 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]] ~ { <> 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 { <> 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.