<> <> <> <> 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; <> ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; Object: TYPE = Pipal.Object; <> 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]; }; <> 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]; }; <> 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]; }; <> 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]; }; <> 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]; }; <<>> <> 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]; }; <> <> <<- 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[]; <> 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]; }; <> 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]]; }; <> 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]]; }; <> 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; }; <> 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]; }; <> 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]; }; <> 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]; }; <> 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; }; <> 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]; <> 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]; <> 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.