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 18, 1988 11:46:11 am PDT
DIRECTORY
BasicTime, BrineIO, CedarProcess, FileStream, FS, ImagerTransformation, IO,
Pipal, PipalInt, PipalIO, PipalOps, PipalReal,
Process, RefTab, RefTabExtras, Rope, RopeHash, SymTab;
PipalIOImpl: CEDAR PROGRAM
IMPORTS BasicTime, BrineIO, CedarProcess, FileStream, FS, ImagerTransformation, IO, Pipal, PipalInt, PipalOps, Process, RefTab, RefTabExtras, Rope, RopeHash, 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: ROPENARROW [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: STREAMFS.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.ROPENIL] = {
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"]
];
};
HashImport: Pipal.HashProc = {
import: Import ← NARROW [object];
hash ← RopeHash.FromRope[import.name];
};
EqualImport: Pipal.EqualProc = {
import1: Import ← NARROW [object1];
import2: Import ← NARROW [object2];
IF NOT Rope.Equal[import1.name, import2.name] THEN RETURN [FALSE];
IF import1.referee#NIL AND import2.referee#NIL THEN RETURN [Pipal.Equal[import1.referee, import2.referee]];
IF import1.creationTime#BasicTime.nullGMT AND import2.creationTime#BasicTime.nullGMT THEN RETURN [import1.creationTime=import2.creationTime];
IF import1.referee=NIL THEN BindImport[import1];
IF import2.referee=NIL THEN BindImport[import2];
equal ← Pipal.Equal[import1.referee, import2.referee];
};
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: BOOLFALSE, creationTime: BasicTime.GMT] RETURNS [import: Import] = {
import ← NEW [ImportRec ← [name: name, isDirectory: isDirectory, creationTime: creationTime]];
};
CreateRefereedImport: PUBLIC PROC [name: Pipal.ROPE, isDirectory: BOOLFALSE, 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: BOOLFALSE] = {
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 CARDNARROW [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: REFSELECT 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, Pipal.hashMethod, NEW [Pipal.HashProc ← PipalOps.CachedHashByEnumeration]];
Pipal.PutClassMethod[directoryClass, Pipal.equalMethod, NEW [Pipal.EqualProc ← PipalOps.EqualByEnumeration]];
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, Pipal.hashMethod, NEW [Pipal.HashProc ← HashImport]];
Pipal.PutClassMethod[importClass, Pipal.equalMethod, NEW [Pipal.EqualProc ← EqualImport]];
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.