PipalIOImpl.mesa
Copyright Ó 1988 by Xerox Corporation. All rights reversed.
Created by Bertrand Serlet, May 2, 1988 6:06:45 pm PDT
Bertrand Serlet, May 13, 1988 2:04:22 pm PDT
DIRECTORY
BasicTime, BrineIO, CedarProcess, FileStream, FS, ImagerTransformation, IO,
Pipal, PipalInt, PipalIO, PipalOps, PipalReal,
Process, RefTab, RefTabExtras, Rope, SymTab;
PipalIOImpl:
CEDAR
PROGRAM
IMPORTS BasicTime, BrineIO, CedarProcess, FileStream, FS, ImagerTransformation, IO, Pipal, PipalInt, PipalOps, Process, RefTab, RefTabExtras, Rope, SymTab
EXPORTS PipalIO =
BEGIN OPEN BrineIO, PipalIO;
Prerequisites
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
Object: TYPE = Pipal.Object;
Class Registration
classRegistry: SymTab.Ref ← SymTab.Create[];
ClassData: TYPE = REF ClassDataRec;
ClassDataRec:
TYPE =
RECORD [read: ClassReadProc, write: ClassWriteProc];
RegisterClass:
PUBLIC
PROC [class: Pipal.Class, read: ClassReadProc, write: ClassWriteProc] = {
data: ClassData ← NEW [ClassDataRec ← [read, write]];
IF write=NIL OR read=NIL THEN ERROR; -- if only one NIL then invariant for object write/read is broken
[] ← SymTab.Store[classRegistry, Pipal.ClassName[class], data];
};
PipalInt Data Types
ReadIntVector:
PUBLIC
PROC [stream:
STREAM]
RETURNS [vector: PipalInt.Vector] = {
vector.x ← ReadInt[stream];
vector.y ← ReadInt[stream];
};
WriteIntVector:
PUBLIC
PROC [stream:
STREAM, vector: PipalInt.Vector] = {
WriteInt[stream, vector.x];
WriteInt[stream, vector.y];
};
ReadIntRectangle:
PUBLIC
PROC [stream:
STREAM]
RETURNS [rectangle: PipalInt.Rectangle] = {
rectangle.base ← ReadIntVector[stream];
rectangle.size ← ReadIntVector[stream];
};
WriteIntRectangle:
PUBLIC
PROC [stream:
STREAM, rectangle: PipalInt.Rectangle] = {
WriteIntVector[stream, rectangle.base];
WriteIntVector[stream, rectangle.size];
};
ReadOrientation:
PUBLIC
PROC [stream:
STREAM]
RETURNS [orientation: PipalInt.Orientation] = {
aux: [ORD[FIRST[PipalInt.Orientation]] .. ORD[LAST[PipalInt.Orientation]]] ← NAT [ReadInt[stream]];
orientation ← LOOPHOLE [aux];
};
WriteOrientation:
PUBLIC
PROC [stream:
STREAM, orientation: PipalInt.Orientation] = {
WriteInt[stream, ORD [orientation]];
};
ReadIntTransformation:
PUBLIC
PROC [stream:
STREAM]
RETURNS [transformation: PipalInt.Transformation] = {
transformation.translation ← ReadIntVector[stream];
transformation.orientation ← ReadOrientation[stream];
};
WriteIntTransformation:
PUBLIC
PROC [stream:
STREAM, transformation: PipalInt.Transformation] = {
WriteIntVector[stream, transformation.translation];
WriteOrientation[stream, transformation.orientation];
};
PipalReal Data Types
ReadRealVector:
PUBLIC
PROC [stream:
STREAM]
RETURNS [vector: PipalReal.Vector] = {
vector.x ← ReadReal[stream];
vector.y ← ReadReal[stream];
};
WriteRealVector:
PUBLIC
PROC [stream:
STREAM, vector: PipalReal.Vector] = {
WriteReal[stream, vector.x];
WriteReal[stream, vector.y];
};
ReadRealRectangle:
PUBLIC
PROC [stream:
STREAM]
RETURNS [rectangle: PipalReal.Rectangle] = {
rectangle.base ← ReadRealVector[stream];
rectangle.size ← ReadRealVector[stream];
};
WriteRealRectangle:
PUBLIC
PROC [stream:
STREAM, rectangle: PipalReal.Rectangle] = {
WriteRealVector[stream, rectangle.base];
WriteRealVector[stream, rectangle.size];
};
ReadRealTransformation:
PUBLIC
PROC [stream:
STREAM]
RETURNS [transformation: PipalReal.Transformation] = {
a: REAL ← ReadReal[stream];
b: REAL ← ReadReal[stream];
c: REAL ← ReadReal[stream];
d: REAL ← ReadReal[stream];
e: REAL ← ReadReal[stream];
f: REAL ← ReadReal[stream];
transformation ← ImagerTransformation.Create[a, b, c, d, e, f];
};
WriteRealTransformation:
PUBLIC
PROC [stream:
STREAM, transformation: PipalReal.Transformation] = {
WriteReal[stream, transformation.a]; WriteReal[stream, transformation.b]; WriteReal[stream, transformation.c];
WriteReal[stream, transformation.d]; WriteReal[stream, transformation.e]; WriteReal[stream, transformation.f];
};
Object Class IO Utilities
ReadObject:
PUBLIC
PROC [stream:
STREAM]
RETURNS [object: Object] = {
objects: RefTab.Ref ← GetRefTab[stream, $PipalIOObjectsTable, RefTabExtras.EqualRope, RefTabExtras.HashRope];
objectID: ROPE ← ReadID[stream];
IF Rope.Fetch[objectID]#'O THEN ERROR;
object ← NARROW [RefTab.Fetch[objects, objectID].val];
IF object=
NIL
THEN {
className: Pipal.ROPE ← ReadRope[stream];
classData: ClassData ← NARROW [SymTab.Fetch[classRegistry, className].val];
bbox: PipalInt.Rectangle ← ReadIntRectangle[stream];
abutBox: PipalInt.Rectangle ← ReadIntRectangle[stream];
CedarProcess.CheckAbort[];
Process.Yield[];
object ← classData.read[stream];
IF PipalInt.BBox[object, []]#bbox THEN ERROR;
IF PipalInt.AbutBox[object]#abutBox THEN ERROR;
IF NOT RefTab.Insert[objects, objectID, object] THEN ERROR;
};
};
WriteObject:
PUBLIC
PROC [stream:
STREAM, object: Object] = {
objects: RefTab.Ref ← GetRefTab[stream, $PipalIOObjectsTable];
objectID: ROPE ← NARROW [RefTab.Fetch[objects, object].val];
IF objectID=
NIL
THEN {
className: Pipal.ROPE = Pipal.ClassName[Pipal.ObjectClass[object]];
classData: ClassData ← NARROW [SymTab.Fetch[classRegistry, className].val];
CedarProcess.CheckAbort[];
Process.Yield[];
objectID ← MakeID["O", RefTab.GetSize[objects]];
IF NOT RefTab.Insert[objects, object, objectID] THEN ERROR;
WriteID[stream, objectID];
WriteRope[stream, className];
WriteIntRectangle[stream, PipalInt.BBox[object, []]];
WriteIntRectangle[stream, PipalInt.AbutBox[object]];
classData.write[stream, object];
}
ELSE WriteID[stream, objectID];
};
Pipal Files
FileInfo:
PUBLIC
PROC [fileName: Pipal.
ROPE, wantedCreatedTime: BasicTime.
GMT ← BasicTime.nullGMT]
RETURNS [creationTime: BasicTime.
GMT ← BasicTime.nullGMT] = {
creationTime ← FS.FileInfo[name: Rope.Cat[fileName, ".pipal"], wantedCreatedTime: wantedCreatedTime ! ANY => GOTO Fails].created;
EXITS Fails => RETURN [BasicTime.nullGMT];
};
OpenFileStream:
PROC [fileName:
ROPE]
RETURNS [stream:
STREAM, creationTime: BasicTime.
GMT] = {
fileHandle: FS.OpenFile ← FS.Create[name: Rope.Cat[fileName, ".pipal"]];
creationTime ← FS.GetInfo[fileHandle].created;
stream ← FileStream.StreamFromOpenFile[openFile: fileHandle, accessRights: $write];
};
RestoreObject:
PUBLIC
PROC [fileName:
ROPE, wantedCreatedTime: BasicTime.
GMT ← BasicTime.nullGMT]
RETURNS [object: Object] = {
stream: STREAM ← FS.StreamOpen[fileName: Rope.Cat[fileName, ".pipal"], wantedCreatedTime: wantedCreatedTime];
object ← ReadObject[stream];
IO.Close[stream];
};
SaveObject:
PUBLIC
PROC [fileName:
ROPE, object: Object]
RETURNS [creationTime: BasicTime.
GMT] = {
stream: STREAM;
[stream, creationTime] ← OpenFileStream[fileName];
WriteObject[stream, object];
IO.Close[stream];
};
Directory
directoryClass:
PUBLIC Pipal.Class ← Pipal.RegisterClass[name: "Directory", type:
CODE [DirectoryRec]];
DescribeDirectory: Pipal.DescribeProc = {
directory: Directory ← NARROW [object];
Pipal.PutIndent[out, indent, cr];
IO.PutF[out, "Directory %g: %g nodes", IO.rope[directory.name], IO.int[SymTab.GetSize[directory.table]]];
};
EnumerateIntDirectory: PipalInt.EnumerateProc = {
directory: Directory ← NARROW [object];
quit ← each[transformation, directory.child];
};
EnumerateDirectory: PipalOps.EnumerateProc = {
EachPair: SymTab.EachPairAction = {quit ← each[val]};
directory: Directory ← NARROW [object];
quit ← each[directory.child] OR SymTab.Pairs[directory.table, EachPair];
};
CreateDirectory:
PUBLIC
PROC [name: Pipal.
ROPE, child: Pipal.Object, table: SymTab.Ref]
RETURNS [directory: Directory] = {
directory ← NEW [DirectoryRec ← [name: name, child: child, table: table]];
};
ReplaceDirectory: PipalOps.ReplaceProc = {
EachPair: SymTab.EachPairAction = {[] ← SymTab.Store[table, key, map[val]]};
directory: Directory ← NARROW [parent];
table: SymTab.Ref ← SymTab.Create[(SymTab.GetSize[directory.table]/2)*2+1];
[] ← SymTab.Pairs[directory.table, EachPair];
newParent ← CreateDirectory[directory.name, map[directory.child], table];
};
WriteDirectory: ClassWriteProc = {
EachPair: SymTab.EachPairAction = {
WriteRope[stream, key];
WriteObject[stream, val];
};
directory: Directory ← NARROW [object];
WriteRope[stream, directory.name];
WriteObject[stream, directory.child];
WriteInt[stream, SymTab.GetSize[directory.table]];
[] ← SymTab.Pairs[directory.table, EachPair];
};
ReadDirectory: ClassReadProc = {
name: Pipal.ROPE ← ReadRope[stream];
child: Pipal.Object ← ReadObject[stream];
size: INT ← ReadInt[stream];
table: SymTab.Ref ← SymTab.Create[(size/2)*2+1];
THROUGH [0 .. size)
DO
name: Pipal.ROPE ← ReadRope[stream];
object: Pipal.Object ← ReadObject[stream];
[] ← SymTab.Store[table, name, object];
ENDLOOP;
IF SymTab.GetSize[table]#size THEN ERROR;
object ← CreateDirectory[name, child, table];
};
FetchInDirectory:
PUBLIC
PROC [directory: Directory, name: Pipal.
ROPE]
RETURNS [object: Pipal.Object ←
NIL] = {
object ← SymTab.Fetch[directory.table, name].val;
};
DirectoryAndShortName:
PUBLIC
PROC [name: Pipal.
ROPE]
RETURNS [directory, shortName: Pipal.
ROPE ←
NIL] = {
index: INT ← Rope.Find[name, "."];
IF index<0 OR index=Rope.Length[name]-1 THEN RETURN;
directory ← Rope.Substr[name, 0, index];
shortName ← Rope.Substr[name, index+1];
};
Imports
Invariants for imports are quite complex. But several observations makes that mess manageable:
- when isDirectory is TRUE, only the IntEnumeration and the Replacement act funny, the referee and creationTime are exactly like any regular object, although they refer to the directory itself.
- there is a strict ordering for all imports of the same name (and the same isDirectory) by decreasing creationTime. When the creationTime is nullGMT, the referee has to be present, and only one such import can exist. It means Now[] and is more recent than any creationTime.
importClass:
PUBLIC Pipal.Class ← Pipal.RegisterClass[name: "Import", type:
CODE [ImportRec]];
FileName:
PROC [name: Pipal.
ROPE, isDirectory:
BOOL]
RETURNS [fileName: Pipal.
ROPE] = {
fileName ←
IF isDirectory
THEN Rope.Cat[DirectoryAndShortName[name].directory, ".directory"]
ELSE name;
};
DescribeImport: Pipal.DescribeProc = {
import: Import ← NARROW [object];
Pipal.PutIndent[out, indent, cr];
IO.PutF[
out, "Import of %g (%g%g %g)",
IO.rope[import.name],
IO.rope[IF import.isDirectory THEN "dir " ELSE NIL],
IO.int[IF import.creationTime=BasicTime.nullGMT THEN 0 ELSE BasicTime.ToNSTime[import.creationTime]],
IO.rope[IF import.referee=NIL THEN NIL ELSE "bound"]
];
};
EnumerateIntImport: PipalInt.EnumerateProc = {
import: Import ← NARROW [object];
IF import.referee=NIL THEN BindImport[import];
quit ← each[
transformation,
IF import.isDirectory
THEN FetchInDirectory[NARROW [import.referee], DirectoryAndShortName[import.name].shortName]
ELSE import.referee
];
};
EnumerateOpsImport: PipalOps.EnumerateProc = {
import: Import ← NARROW [object];
IF import.referee=NIL THEN BindImport[import];
quit ← each[import.referee];
};
BindImport:
PROC [import: Import] = {
object: Pipal.Object ← RestoreObject[FileName[import.name, import.isDirectory], import.creationTime];
import.referee ← object;
IF import.isDirectory AND NOT ISTYPE [object, Directory] THEN ERROR;
};
CreateTimedImport:
PUBLIC
PROC [name: Pipal.
ROPE, isDirectory:
BOOL ←
FALSE, creationTime: BasicTime.
GMT]
RETURNS [import: Import] = {
import ← NEW [ImportRec ← [name: name, isDirectory: isDirectory, creationTime: creationTime]];
};
CreateRefereedImport:
PUBLIC
PROC [name: Pipal.
ROPE, isDirectory:
BOOL ←
FALSE, referee: Pipal.Object]
RETURNS [import: Import] = {
IF isDirectory AND NOT ISTYPE [referee, Directory] THEN ERROR;
import ← NEW [ImportRec ← [name: name, isDirectory: isDirectory, referee: referee]];
};
EnumerateObjectImports:
PUBLIC
PROC [object: Pipal.Object, each: EachImportProc]
RETURNS [quit:
BOOL ←
FALSE] = {
Each: PipalOps.EachChildProc = {quit ← EnumerateObjectImports[child, each]};
WITH object
SELECT
FROM
import: Import => quit ← each[import];
ENDCASE =>
IF PipalOps.HasEnumerate[object]
THEN quit ← PipalOps.Enumerate[object, Each];
};
ReplaceImport: PipalOps.ReplaceProc = {
import: Import ← NARROW [parent];
IF import.referee=NIL THEN BindImport[import];
newParent ← CreateRefereedImport[import.name, import.isDirectory, map[import.referee]];
};
savedImports: RefTab.Ref ← RefTab.Create[];
maps all import referees saved or being saved (a REF creationTime). This avoids saving twice the same import.
WriteImport: ClassWriteProc = {
import: Import ← NARROW [object];
SELECT
TRUE
FROM
import.creationTime#BasicTime.nullGMT => {};
RefTab.Fetch[savedImports, import.referee].found => {
refNS: REF CARD ← NARROW [RefTab.Fetch[savedImports, import.referee].val];
import.creationTime ← BasicTime.FromNSTime[refNS^];
};
ENDCASE => {
fileName: ROPE ← FileName[import.name, import.isDirectory];
stream: STREAM;
creationTime: BasicTime.GMT;
[stream, creationTime] ← OpenFileStream[fileName];
[] ← RefTab.Store[savedImports, object, NEW [CARD ← BasicTime.ToNSTime[creationTime]]];
WriteObject[stream, import.referee];
IO.Close[stream];
import.creationTime ← creationTime;
};
WriteRope[stream, import.name];
WriteBool[stream, import.isDirectory];
WriteGMT[stream, import.creationTime];
};
ReadImport: ClassReadProc = {
name: Pipal.ROPE ← ReadRope[stream];
isDirectory: BOOL ← ReadBool[stream];
creationTime: BasicTime.GMT ← ReadGMT[stream];
object ← CreateTimedImport[name, isDirectory, creationTime];
};
Imports + Directories
Fetch:
PUBLIC
PROC [name: Pipal.
ROPE]
RETURNS [object: Pipal.Object ←
NIL] = {
creationTime: BasicTime.GMT ← FileInfo[name];
IF creationTime#BasicTime.nullGMT THEN RETURN [CreateTimedImport[name, FALSE, creationTime]];
creationTime ← FileInfo[FileName[name, TRUE]];
IF creationTime#BasicTime.nullGMT THEN RETURN [CreateTimedImport[name, TRUE, creationTime]];
};
Utility
ReadObjects:
PUBLIC
PROC [stream:
STREAM]
RETURNS [objects: Pipal.Objects ←
NIL] = {
size: NAT = ReadInt[stream];
THROUGH [0 .. size) DO objects ← CONS [ReadObject[stream], objects] ENDLOOP;
RETURN [Pipal.Reverse[objects]];
};
Overlay
ReadOverlay: ClassReadProc = {
object ← Pipal.CreateOverlay[ReadObjects[stream]];
};
WriteOverlay: ClassWriteProc = {
overlay: Pipal.Overlay ← NARROW [object];
WriteInt[stream, overlay.size];
FOR i: NAT IN [0 .. overlay.size) DO WriteObject[stream, overlay[i]] ENDLOOP;
};
Icon
ReadIcon: ClassReadProc = {
reference: Pipal.Object ← ReadObject[stream];
referent: Pipal.Object ← ReadObject[stream];
object ← Pipal.CreateIcon[reference, referent];
};
WriteIcon: ClassWriteProc = {
icon: Pipal.Icon ← NARROW [object];
WriteObject[stream, icon.reference]; WriteObject[stream, icon.referent];
};
Annotation
ReadAnnotation: ClassReadProc = {
key: ATOM ← ReadAtom[stream];
valueKey: ATOM ← ReadAtom[stream];
value:
REF ←
SELECT valueKey
FROM
$Null => NIL,
$Atom => ReadAtom[stream],
$Rope => ReadRope[stream],
$Ropes => ReadRopes[stream],
$Rectangle => NEW [PipalInt.Rectangle ← ReadIntRectangle[stream]],
ENDCASE => ERROR;
object ← Pipal.CreateAnnotation[ReadObject[stream], key, value];
};
WriteAnnotation: ClassWriteProc = {
annotation: Pipal.Annotation ← NARROW [object];
WriteAtom[stream, annotation.key];
IF annotation.value=
NIL
THEN WriteAtom[stream, $Null]
ELSE
WITH annotation.value
SELECT
FROM
atom:
ATOM => {
WriteAtom[stream, $Atom];
WriteAtom[stream, atom];
};
rope: Pipal.
ROPE => {
WriteAtom[stream, $Rope];
WriteRope[stream, rope];
};
ropes:
LIST
OF Pipal.
ROPE => {
WriteAtom[stream, $Ropes];
WriteRopes[stream, ropes];
};
refRect:
REF PipalInt.Rectangle => {
WriteAtom[stream, $Rectangle];
WriteIntRectangle[stream, refRect^];
};
ENDCASE => ERROR;
WriteObject[stream, annotation.child];
};
All Transformations
ReadTransform: ClassReadProc = {
transformation: PipalInt.Transformation ← ReadIntTransformation[stream];
object ← PipalInt.TransformObject[transformation, ReadObject[stream]];
};
WriteTransform: ClassWriteProc = {
trans: PipalInt.Transformation; sub: Pipal.Object ← NIL;
IF PipalInt.CountChildren[object]#1 THEN ERROR;
[trans, sub] ← PipalInt.NthChild[object];
WriteIntTransformation[stream, trans];
WriteObject[stream, sub];
};
Abut
ReadAbut: ClassReadProc = {
inX: BOOL = ReadBool[stream];
object ← PipalInt.CreateAbut[inX, ReadObjects[stream]];
};
WriteAbut: ClassWriteProc = {
abut: PipalInt.Abut ← NARROW [object];
WriteBool[stream, abut.inX];
WriteInt[stream, abut.size];
FOR i: NAT IN [0 .. abut.size) DO WriteObject[stream, abut[i]] ENDLOOP;
};
Directory Initialization
Pipal.PutClassMethod[directoryClass, Pipal.describeMethod, NEW [Pipal.DescribeProc ← DescribeDirectory]];
Pipal.PutClassMethod[directoryClass, PipalInt.enumerateMethod, NEW [PipalInt.EnumerateProc ← EnumerateIntDirectory]];
Pipal.PutClassMethod[directoryClass, PipalOps.enumerateMethod, NEW [PipalOps.EnumerateProc ← EnumerateDirectory]];
Pipal.PutClassMethod[directoryClass, PipalOps.replaceMethod, NEW [PipalOps.ReplaceProc ← ReplaceDirectory]];
RegisterClass[directoryClass, ReadDirectory, WriteDirectory];
Import Initialization
Pipal.PutClassMethod[importClass, Pipal.describeMethod, NEW [Pipal.DescribeProc ← DescribeImport]];
Pipal.PutClassMethod[importClass, PipalInt.enumerateMethod, NEW [PipalInt.EnumerateProc ← EnumerateIntImport]];
Pipal.PutClassMethod[importClass, PipalOps.enumerateMethod, NEW [PipalOps.EnumerateProc ← EnumerateOpsImport]];
Pipal.PutClassMethod[importClass, PipalOps.replaceMethod, NEW [PipalOps.ReplaceProc ← ReplaceImport]];
RegisterClass[importClass, ReadImport, WriteImport];
Initialization
RegisterClass[Pipal.overlayClass, ReadOverlay, WriteOverlay];
RegisterClass[Pipal.iconClass, ReadIcon, WriteIcon];
RegisterClass[Pipal.annotationClass, ReadAnnotation, WriteAnnotation];
RegisterClass[PipalInt.transformClass, ReadTransform, WriteTransform];
RegisterClass[PipalInt.translationClass, ReadTransform, WriteTransform];
RegisterClass[PipalInt.orientClass, ReadTransform, WriteTransform];
RegisterClass[PipalInt.abutClass, ReadAbut, WriteAbut];
END.