<> <> <> DIRECTORY AMEvents USING [Debugging, Debugged], CIFS USING [Error], CedarSnapshot USING [CheckpointProc, Register, RollbackProc, After], Convert USING [Value, ValueToRope], DB, DBNames, Directory USING[Lookup, ignore, Error, GetProps], File USING[ Capability ], IO USING [GetCard, EndOfStream, Close, CR, CreateFilterCommentsStream, GetChar, GetIndex, int, PeekChar, PutF, rope, STREAM, time, TAB, SP, GetToken, SkipOver, TokenProc, BreakProc, WhiteSpace, GreenwichMeanTime, CharProc, GetSequence], DBIcons USING [Failure], Icons USING [NewIconFromFile, IconFlavor], FileIO USING [Open, StreamFromCapability, OpenFailed], MessageWindow USING [Append, Blink], PageFault USING [AddressFault], Process USING [Detach], Rope USING [Cat, Equal, ROPE, ToRefText], System USING[ GreenwichMeanTime, GetGreenwichMeanTime ], ViewerClasses USING [Viewer], ViewerEvents USING [EventProc, RegisterEventProc], ViewerOps USING [FindViewer, RestoreViewer] ; DBIconsImpl: CEDAR MONITOR IMPORTS AMEvents, CedarSnapshot, CIFS, Convert, FileIO, IO, MessageWindow, PageFault, Process, Rope, Icons, ViewerEvents, ViewerOps, DB, DBNames, Directory, System EXPORTS DBIcons = BEGIN OPEN DB, Rope; <> Failed: PUBLIC ERROR [why: DBIcons.Failure, reason: Rope.ROPE] = CODE; <> IconDomain: DB.Domain; -- the domain of icons IconRelation: DB.Relation; -- the relation giving the properties of an icon <> IconFileAttr: DB.Attribute; -- the name of the file for an icon IconIndexAttr: DB.Attribute; -- the index in the file IconIsAttr: DB.Attribute; -- the icon itself (the other attributes are treated as properties) IconFlavorAttr: DB.Attribute; -- the flavor of the icon (this is somewhat tricky) IconDBInfo: DB.Relation; -- the information about the creation of the database <> IconDBTime: DB.Attribute; -- the last time of a parse (to make sure the DB is consistent with the catalogue; the DB may well be an extension of the parse) <> RegisterIcon: PUBLIC ENTRY PROC [iconName: Rope.ROPE, fileName: Rope.ROPE, index: CARDINAL, saveInCatalogue: BOOL _ FALSE] = { <> InternalRegisterIcon[ iconName, fileName, index ]; IF saveInCatalogue THEN WriteEntry[iconName, fileName, index]; DB.MarkTransaction[DB.TransactionOf[$Icons]] }; GetIcon: PUBLIC PROC [iconName: Rope.ROPE, default: Icons.IconFlavor _ unInit] RETURNS [Icons.IconFlavor] = { <> <> icon: Entity = DeclareEntity[IconDomain, iconName, OldOnly]; IF icon # NIL THEN { flavor: Icons.IconFlavor _ V2U[ GetP[ icon, IconFlavorAttr, IconIsAttr ] ]; fileName: ROPE = V2S[ GetP[ icon, IconFileAttr, IconIsAttr ] ]; index: CARDINAL = V2U[ GetP[ icon, IconIndexAttr, IconIsAttr ] ]; IF flavor = unInit THEN { flavor _ Icons.NewIconFromFile[fileName, index ! CIFS.Error => IF code # noSuchFile THEN REJECT ELSE IF default = unInit THEN GOTO Default ELSE ERROR Failed[fileNotFound, fileName]; PageFault.AddressFault => IF default # unInit THEN GOTO Default ELSE ERROR Failed[invalidIndex, Convert.ValueToRope[[signed[index]]]]; ]; -- NewIconFromFile raises this if given badindex. [] _ SetP[ icon, IconFlavorAttr, U2V[flavor], IconIsAttr ] }; RETURN[flavor]; }; IF default # unInit THEN GOTO Default; ERROR Failed[noSuchIcon, iconName]; EXITS Default => RETURN[default]; }; IsRegistered: PUBLIC ENTRY PROC[iconName: Rope.ROPE _ NIL, fileName: Rope.ROPE _ NIL, index: CARDINAL _ LAST[CARDINAL]] RETURNS[name: Rope.ROPE, file: Rope.ROPE, i: CARDINAL] = { <> IF iconName # NIL THEN { icon: Entity = DeclareEntity[IconDomain, iconName, OldOnly]; IF icon = NIL THEN RETURN[NIL, NIL, 0]; { file _ V2S[ GetP[ icon, IconFileAttr, IconIsAttr ] ]; i _ V2U[ GetP[ icon, IconIndexAttr, IconIsAttr ] ] } } ELSE {iconRels: RelshipSet = RelationSubset[IconRelation, LIST[AttributeValue[IconFileAttr, S2V[file]], AttributeValue[IconIndexAttr, U2V[index]]]]; iconRel: Relship = NextRelship[iconRels]; IF iconRel = NIL THEN { ReleaseRelshipSet[iconRels]; RETURN[NIL, NIL, 0] }; name _ DB.NameOf[V2E[GetF[iconRel, IconIsAttr]]]; file _ fileName; i _ index; ReleaseRelshipSet[iconRels] } }; InvalidateCache: PUBLIC ENTRY PROC [iconName: Rope.ROPE _ NIL] = { <> icons: EntitySet = DomainSubset[ IconDomain, iconName ]; FOR e: Entity _ NextEntity[icons], NextEntity[icons] UNTIL e = NIL DO [] _ SetP[ e, IconFlavorAttr, U2V[Icons.IconFlavor[unInit]], IconIsAttr ] ENDLOOP; ReleaseEntitySet[ icons ]; DB.MarkTransaction[DB.TransactionOf[$Icons]] }; InternalRegisterIcon: INTERNAL PROC[ iconName: Rope.ROPE, fileName: Rope.ROPE, index: CARDINAL ] = { icon: DB.Entity = DeclareEntity[IconDomain, iconName]; avList: AttributeValueList = LIST[AttributeValue[IconIsAttr, icon], AttributeValue[IconFileAttr, S2V[fileName]], AttributeValue[IconIndexAttr, U2V[index]]]; [] _ DeclareRelship[ IconRelation, avList ]; [] _ SetP[ icon, IconFlavorAttr, U2V[Icons.IconFlavor[unInit]], IconIsAttr ] }; <> catalogueName: Rope.ROPE _ "DBIcons.Catalogue"; catalogueCap: File.Capability; Parse: INTERNAL PROCEDURE = { OPEN IO; stream: IO.STREAM; { ENABLE { Failed => REJECT; IO.EndOfStream => GOTO Out; < GOTO Out;>> }; stream _ CreateFilterCommentsStream[FileIO.StreamFromCapability[ fileName: catalogueName, capability: catalogueCap, accessOptions: read ! FileIO.OpenFailed => ERROR Failed[noCatalogue, catalogueName]]]; DO sepCount: INT; DBNameProc: IO.CharProc = { <> <> IF char = DBNames.Separator THEN { sepCount _ sepCount+1; RETURN[FALSE, TRUE] }; IF char = ': AND (sepCount = 2 OR sepCount = 0) THEN RETURN[TRUE, FALSE]; RETURN[FALSE, TRUE] }; whiteSpace: IO.BreakProc = { RETURN[IF char = CR THEN break ELSE IO.WhiteSpace[char]] }; isACR: IO.BreakProc = { RETURN[IF char = CR THEN break ELSE other] }; tokenProc: IO.BreakProc = { RETURN[SELECT char FROM IO.SP, IO.TAB, ', => sepr, IO.CR => break, ENDCASE => other ]; }; iconName, fileName: Rope.ROPE; index: CARDINAL; position: INT; IO.SkipOver[stream, IO.WhiteSpace]; position _ stream.GetIndex[]; sepCount _ 0; iconName _ IO.GetSequence[stream, DBNameProc]; <<-- see if key is followed by a :>> IO.SkipOver[stream, whiteSpace]; -- skips over spaces, tabs, but stops at CR IF stream.PeekChar[] # ': THEN { IO.SkipOver[stream, isACR]; Report1[msg: Rope.Cat["missing : at [", Convert.ValueToRope[[signed[position]]], "]"]]; LOOP; }; [] _ stream.GetChar[]; -- the : IO.SkipOver[stream, whiteSpace]; fileName _ IO.GetToken[stream, tokenProc]; index _ IO.GetCard[stream]; InternalRegisterIcon[ iconName, fileName, index ] ENDLOOP; EXITS Out => NULL; }; IF stream # NIL THEN stream.Close[]; IF errorLog # NIL THEN { MessageWindow.Append["problems encountered, see IconRegistry.log."]; MessageWindow.Blink[]; errorLog.Close[]; errorLog _ NIL; }; }; WriteEntry: INTERNAL PROC [iconName: Rope.ROPE, fileName: Rope.ROPE, index: CARDINAL] = { OPEN IO; viewer: ViewerClasses.Viewer; stream: IO.STREAM _ FileIO.Open[fileName: catalogueName, accessOptions: append, createOptions: oldOnly]; Write: PROC [iconName: Rope.ROPE, fileName: Rope.ROPE, index: CARDINAL] = { stream.PutF["\n%g: %g %d", rope[iconName], rope[fileName], int[index]]; }; IF iconName # NIL THEN Write[iconName, fileName, index]; stream.Close[]; IF (viewer _ ViewerOps.FindViewer[catalogueName]) # NIL THEN ViewerOps.RestoreViewer[viewer]; }; WriteCatalogue: PUBLIC ENTRY PROC [] = { icons: EntitySet = DomainSubset[ IconDomain]; FOR e: Entity _ NextEntity[icons], NextEntity[icons] UNTIL e = NIL DO { fileName: ROPE = V2S[ GetP[ e, IconFileAttr, IconIsAttr ] ]; index: CARDINAL = V2U[ GetP[ e, IconIndexAttr, IconIsAttr ] ]; WriteEntry[ DBNames.EntityToName[e], fileName, index ] } ENDLOOP; ReleaseEntitySet[icons] }; <> Report1: INTERNAL PROCEDURE [msg: Rope.ROPE] = { OPEN IO; ENABLE { UNWIND, AMEvents.Debugging, AMEvents.Debugged => NULL; ANY => CONTINUE; }; IF errorLog = NIL THEN { errorLog _ FileIO.Open[fileName: "IconRegistry.log", accessOptions: overwrite]; errorLog.PutF["Processing %g at %t", rope[catalogueName], time[]]; }; errorLog.PutF["\n\n%g", rope[msg]]; }; errorLog: IO.STREAM _ NIL; <> NoticeChanges: CedarSnapshot.RollbackProc = { DB.Initialize[nCachePages: 256]; SetUpSegment[segmentFile: "[Local]Icons.segment", seg: $Icons, number: 140B]; IconDomain _ DeclareDomain["Icon", $Icons]; IconRelation _ DeclareRelation[ "IconInfo", $Icons ]; IconFileAttr _ DeclareAttribute[IconRelation, "file", StringType]; IconIndexAttr _ DeclareAttribute[IconRelation, "index", IntType]; IconIsAttr _ DeclareAttribute[IconRelation, "of", IconDomain, Key]; IconFlavorAttr _ DeclareAttribute[IconRelation, "flavor", IntType]; IconDBInfo _ DeclareRelation[ "IconDBInfo", $Icons ]; IconDBTime _ DeclareAttribute[IconDBInfo, "time", TimeType]; InvalidateCache[]; ParseCatalogue[]}; SetUpSegment: PROC[segmentFile: ROPE, seg: DB.Segment, number: NAT_ 0] = <> { success: BOOL_ TRUE; DB.DeclareSegment[segmentFile, seg, number ! DB.Error => { IF code=TransactionAlreadyOpen THEN GOTO AlreadyDone }]; DB.OpenTransaction[seg ! DB.Error => {IF code=FileNotFound THEN success_ FALSE; CONTINUE}]; IF NOT success THEN { DB.CloseTransaction[DB.TransactionOf[seg]]; -- DB has left the transaction open DB.DeclareSegment[segmentFile, seg, number,, NewOnly]; DB.OpenTransaction[seg]; DB.MarkTransaction[DB.TransactionOf[seg]] }; EXITS AlreadyDone => NULL }; CloseTrans: CedarSnapshot.CheckpointProc = { DB.CloseTransaction[trans: DB.TransactionOf[segment: $Icons]] }; ParseCatalogue: ENTRY PROC = TRUSTED { txt: REF TEXT = Rope.ToRefText[catalogueName]; name: LONG STRING = LOOPHOLE[txt]; failed: BOOLEAN _ FALSE; iconDBInfo: RelshipSet = RelationSubset[ IconDBInfo ]; infoRel: Relation = NextRelship[iconDBInfo]; timeInDB: IO.GreenwichMeanTime = IF infoRel # NIL THEN V2T[GetF[infoRel, IconDBTime]] ELSE System.GetGreenwichMeanTime[]; catalogueCap _ Directory.Lookup[fileName: name, permissions: Directory.ignore ! Directory.Error => {failed _ TRUE; CONTINUE}]; IF failed THEN { ReleaseRelshipSet[iconDBInfo]; ERROR Failed[noCatalogue, catalogueName] }; { createDate: IO.GreenwichMeanTime; [, , createDate, , ] _ Directory.GetProps[file: catalogueCap, name: name]; IF createDate # timeInDB THEN Parse[]; IF infoRel = NIL THEN SetF[ DeclareRelship[ IconDBInfo ], IconDBTime, T2V[createDate] ] ELSE SetF[ infoRel, IconDBTime, T2V[createDate] ]; DB.MarkTransaction[ DB.TransactionOf[ $Icons ] ] }; ReleaseRelshipSet[iconDBInfo] }; WasCatalogueEdited: ViewerEvents.EventProc -- [viewer: ViewerClasses.Viewer, event: ViewerEvent] -- = { IF Rope.Equal[viewer.name, catalogueName, FALSE] THEN TRUSTED {Process.Detach[FORK ParseCatalogue[]]}; }; <> [] _ ViewerEvents.RegisterEventProc[proc: WasCatalogueEdited, event: save, before: FALSE]; TRUSTED {CedarSnapshot.Register[c: CloseTrans, r: NoticeChanges]; NoticeChanges[checkpoint] }; END. <<>>