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;
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"]
];
};
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:
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];
};
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];
};
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];