DIRECTORY BasicTime USING [ GMT, nullGMT, latestGMT, Period, Unpacked, Unpack, Update], DB USING [ Relship, DeclareRelship, SetF, Entity, DeclareEntity, NameOf, MarkTransaction, TransactionOf, RelshipSet, RelationSubset, NextRelship, ReleaseRelshipSet, Aborted, I2V, B2V, T2V, V2E, GetF, AttributeValue, AttributeValueList, DestroyRelship], Hickory USING [ Event, EventTuple, ErrorCode, GroupSet], HickoryCache USING [ InvalidateOneCache, IsInEnteredEvents, DeleteFromEnteredEvents, InsertEvent], HickoryGroup USING [ InsertEventToGroup], HickoryNotify USING [ NotifyClients], HickorySupport USING [ RestartTransaction, StartTransaction, ConvertRopes, ReConvertRopes, P2I, R2I], HickoryStorage USING [ protData], Rope USING [ ROPE, Equal] ; HickoryStoreImpl: CEDAR MONITOR LOCKS HickoryStorage.protData IMPORTS DB, BasicTime, HickorySupport, HickoryStorage, Rope, HickoryGroup, HickoryNotify, HickoryCache EXPORTS Hickory SHARES HickorySupport, HickoryGroup, HickoryNotify, HickoryCache = BEGIN OPEN Hickory, DB, HickoryStorage.protData; Error: PUBLIC SIGNAL [ errCode: Hickory.ErrorCode, arg: Rope.ROPE] = CODE; SameEvent: PUBLIC PROCEDURE [ ev1, ev2: Event] RETURNS [ BOOLEAN] = BEGIN RETURN[ Rope.Equal[ ev1, ev2]]; END; -- SameEvent SameEventTuple: PUBLIC PROCEDURE [ ev1, ev2: EventTuple] RETURNS [ BOOLEAN] = BEGIN IF ( ev1.EventTime = ev2.EventTime) AND ( ev1.Duration = ev2.Duration) AND ( ev1.RepeatType = ev2.RepeatType) AND ( ev1.RepeatUntil = ev2.RepeatUntil) AND ( ev1.Protection = ev2.Protection) AND ( ev1.Remind = ev2.Remind) THEN IF Rope.Equal[ ev1.Text, ev2.Text, FALSE] THEN IF Rope.Equal[ ev1.RepeatTime, ev2.RepeatTime, FALSE] THEN IF Rope.Equal[ ev1.Place, ev2.Place, FALSE] THEN IF Rope.Equal[ ev1.Message, ev2.Message, FALSE] THEN RETURN[ TRUE]; RETURN[ FALSE]; END; -- SameEventTuple EnterEvent: PUBLIC ENTRY PROCEDURE [ ev: EventTuple, groups: GroupSet] RETURNS [ updatedEv: EventTuple] = BEGIN ENABLE UNWIND => NULL; duplicate: BOOLEAN; key: Event; start, end : BasicTime.Unpacked; duration: LONG CARDINAL; IF NOT owner THEN ERROR Error[ NotOwner, NIL]; IF BasicTime.Period[ ev.EventTime, ev.KeepUntil] < 0 THEN ERROR Error[ NotStored, NIL]; start _ BasicTime.Unpack[ ev.EventTime]; duration _ ev.Duration; duration _ 60*duration; end _ BasicTime.Unpack[ BasicTime.Update[ ev.EventTime, duration]]; IF start.day # end.day THEN ERROR Error[ TooLong, NIL]; [ duplicate, key] _ HickoryCache.IsInEnteredEvents[ ev]; IF ev.Key = NIL AND NOT duplicate THEN BEGIN stored: BOOLEAN _ FALSE; ev _ HickorySupport.ConvertRopes[ ev]; IF ev.KeepUntil = BasicTime.nullGMT THEN ev.KeepUntil _ BasicTime.latestGMT; [ ev, stored] _ StoreTuple[ ev]; ev _ HickorySupport.ReConvertRopes[ ev]; IF stored THEN BEGIN HickoryCache.InsertEvent[ ev]; HickoryNotify.NotifyClients[ NewEvent, ev.Key, groups]; IF groups.Head # NIL THEN BEGIN HickoryGroup.InsertEventToGroup[ ev.Key, groups]; HickoryNotify.NotifyClients[ InsertionToGroup, ev.Key, groups]; END; END; END ELSE IF ev.Key = NIL THEN ev.Key _ key; RETURN[ ev]; END; -- EnterEvent ChangeEvent: PUBLIC ENTRY PROCEDURE [ oldEv, newEv: EventTuple] = BEGIN ENABLE UNWIND => NULL; evEnt: DB.Entity; duplicate: BOOLEAN; key: Event; success: BOOLEAN _ FALSE; IF NOT owner THEN ERROR Error[ NotOwner, NIL]; WHILE NOT success DO ENABLE Aborted => { HickorySupport.RestartTransaction; success _ FALSE; LOOP}; HickorySupport.StartTransaction[]; evEnt _ DeclareEntity[ eventDomain, oldEv.Key, OldOnly]; IF evEnt = NIL THEN ERROR Error[ NoSuchEvent, oldEv.Key]; IF NOT Rope.Equal[ oldEv.Key, newEv.Key] THEN ERROR Error[ NotSameKey, NIL]; IF SameEventTuple[ oldEv, newEv] THEN RETURN; [ duplicate, key] _ HickoryCache.IsInEnteredEvents[ oldEv]; IF duplicate THEN HickoryCache.DeleteFromEnteredEvents[ oldEv.Key]; HickoryCache.InsertEvent[ newEv]; -- precaching... oldEv _ HickorySupport.ConvertRopes[ oldEv]; -- NIL > "" newEv _ HickorySupport.ConvertRopes[ newEv]; BEGIN OPEN eventRel; relSet: RelshipSet; av: AttributeValue; tuple: Relship; av _ [ attribute: Key, hi: evEnt, lo: evEnt]; relSet _ RelationSubset[ Rel, LIST[ av]]; -- just one tuple tuple_ NextRelship[ relSet]; IF tuple = NIL THEN RETURN; IF oldEv.EventTime # newEv.EventTime THEN SetF[ tuple, Time, T2V[ LOOPHOLE[ newEv.EventTime]]]; IF oldEv.Duration # newEv.Duration THEN SetF[ tuple, Duration, I2V[ newEv.Duration]]; IF oldEv.RepeatType # None THEN BEGIN DeleteRepeatTuple[ oldEv.Key]; HickoryCache.InvalidateOneCache[ ev: oldEv.Key, erase: TRUE]; END; IF newEv.RepeatType # None THEN BEGIN SetF[ tuple, RepeatType, I2V[ HickorySupport.R2I[ newEv.RepeatType]]]; StoreRepeatTuple[ newEv]; END ELSE SetF[ tuple, RepeatType, I2V[ HickorySupport.R2I[ None]]]; IF newEv.LeadTime # oldEv.LeadTime THEN SetF[ tuple, LeadTime, I2V[ newEv.LeadTime]]; IF newEv.NagTime # oldEv.NagTime THEN SetF[ tuple, NagTime, I2V[ newEv.NagTime]]; IF newEv.KeepUntil # oldEv.KeepUntil THEN SetF[ tuple, KeepTime, T2V[ LOOPHOLE[ newEv.KeepUntil]]]; IF NOT Rope.Equal[ newEv.Text, oldEv.Text] THEN SetF[ tuple, Text, DeclareEntity[ textDomain, newEv.Text, NewOrOld]]; IF NOT Rope.Equal[ newEv.Message, oldEv.Message] THEN SetF[ tuple, Message, DeclareEntity[ messageDomain, newEv.Message, NewOrOld]] ; IF NOT Rope.Equal[ newEv.Place, oldEv.Place] THEN SetF[ tuple, Place, DeclareEntity[ placeDomain, newEv.Place, NewOrOld]]; IF NOT ( Rope.Equal[ newEv.IconLabel, oldEv.IconLabel] AND Rope.Equal[ newEv.IconFlavor, oldEv.IconFlavor]) THEN SetF[ tuple, Icon, EnterIcon[ newEv.IconLabel, newEv.IconFlavor]]; SetF[ tuple, Protection, I2V[ HickorySupport.P2I[ newEv.Protection]]]; SetF[ tuple, Reminder, B2V[ newEv.Remind]]; MarkTransaction[ TransactionOf[ $Hickory]]; success _ TRUE; END; -- OPEN eventRel ENDLOOP; -- Transaction HickoryNotify.NotifyClients[ Edit, newEv.Key, [ NIL, NIL]]; END; -- ChangeEvent StoreTuple: INTERNAL PROCEDURE[ ev: EventTuple] RETURNS [ updatedEv: EventTuple, stored: BOOLEAN] = BEGIN OPEN eventRel; tuple: Relship; key: Event; keyEnt: Entity; duplicate: BOOLEAN _ FALSE; success: BOOLEAN _ FALSE; WHILE NOT success DO ENABLE Aborted => { HickorySupport.RestartTransaction; success _ FALSE; LOOP }; HickorySupport.StartTransaction[]; [ duplicate, key] _ DuplicateTuple[ ev, TRUE]; IF duplicate THEN BEGIN ev.Key _ key; RETURN[ ev, FALSE]; END; keyEnt _ DeclareEntity[ eventDomain, NIL, NewOnly]; --make a new key key _ NameOf[ keyEnt]; tuple _ DeclareRelship[ Rel, NIL, NewOnly]; ev.Key _ key; SetF[ tuple, Key, keyEnt]; SetF[ tuple, Forgotten, B2V[ ev.Forgotten]]; SetF[ tuple, Time, T2V[ LOOPHOLE[ ev.EventTime]]]; SetF[ tuple, Duration, I2V[ ev.Duration]]; SetF[ tuple, RepeatType, I2V[ HickorySupport.R2I[ ev.RepeatType]]]; SetF[ tuple, LeadTime, I2V[ ev.LeadTime]]; SetF[ tuple, NagTime, I2V[ ev.NagTime]]; SetF[ tuple, KeepTime, T2V[ LOOPHOLE[ ev.KeepUntil]]]; SetF[ tuple, Text, DeclareEntity[ textDomain, ev.Text, NewOrOld]]; SetF[ tuple, Message, DeclareEntity[ messageDomain, ev.Message, NewOrOld]] ; SetF[ tuple, Place, DeclareEntity[ placeDomain, ev.Place, NewOrOld]]; SetF[ tuple, Icon, EnterIcon[ ev.IconLabel, ev.IconFlavor]]; SetF[ tuple, Protection, I2V[ HickorySupport.P2I[ ev.Protection]]]; SetF[ tuple, Reminder, B2V[ ev.Remind]]; IF ev.RepeatType # None THEN StoreRepeatTuple[ ev]; MarkTransaction[ TransactionOf[ $Hickory]]; success _ TRUE; ENDLOOP; RETURN[ ev, TRUE]; END; -- StoreTuple DeleteRepeatTuple: INTERNAL PROCEDURE [ ev: Event] = BEGIN OPEN repeatRel; tuple: Relship; evEnt: Entity; relSet: RelshipSet; av: AttributeValue; evEnt _ DeclareEntity[ eventDomain, ev, OldOnly]; IF evEnt = NIL THEN ERROR Error[ NoSuchEvent, ev]; av _ [ attribute: Event, hi: evEnt, lo: evEnt]; relSet _ RelationSubset[ Rel, LIST[ av]]; tuple _ NextRelship[ relSet]; IF tuple # NIL THEN DestroyRelship[ tuple]; ReleaseRelshipSet[ relSet]; END; -- DeleteRepeatTuple StoreRepeatTuple: INTERNAL PROCEDURE[ ev: EventTuple] = BEGIN OPEN repeatRel; tuple: Relship; evEnt: Entity; relSet: RelshipSet; av: AttributeValue; evEnt _ DeclareEntity[ eventDomain, ev.Key, OldOnly]; IF evEnt = NIL THEN ERROR Error[ NoSuchEvent, ev.Key]; av _ [ attribute: Event, hi: evEnt, lo: evEnt]; relSet _ RelationSubset[ Rel, LIST[ av]]; tuple _ NextRelship[ relSet]; IF tuple = NIL THEN tuple _ DeclareRelship[ Rel, NIL, NewOnly]; IF ev.RepeatUntil = BasicTime.nullGMT THEN ev.RepeatUntil _ BasicTime.latestGMT; SetF[ tuple, Event, evEnt]; SetF[ tuple, FirstTime, T2V[ LOOPHOLE[ ev.EventTime]]]; SetF[ tuple, RepeatType, I2V[ HickorySupport.R2I[ ev.RepeatType]]]; SetF[ tuple, RepeatTime, DeclareEntity[ repTimeDomain, ev.RepeatTime, NewOrOld]]; SetF[ tuple, RepeatUntil, T2V[ LOOPHOLE[ ev.RepeatUntil]]]; SetF[ tuple, LastOccurred, T2V[ LOOPHOLE[ ev.EventTime]]]; ReleaseRelshipSet[ relSet]; END; -- StoreRepeatTuple DuplicateTuple: INTERNAL PROCEDURE [ ev: EventTuple, simple: BOOLEAN] RETURNS [ answer: BOOLEAN, key: Event _ NIL] = BEGIN OPEN eventRel; avl: AttributeValueList _ NIL; av: AttributeValue; relSet: RelshipSet; tuple: Relship; textEntity: Entity _ DeclareEntity[ textDomain, ev.Text, OldOnly]; placeEntity: Entity _ DeclareEntity[ placeDomain, ev.Place, OldOnly]; messageEntity: Entity _ DeclareEntity[ messageDomain, ev.Message, OldOnly]; IF textEntity = NIL OR placeEntity = NIL OR messageEntity = NIL THEN RETURN[ FALSE, NIL]; -- otherwise we don't know yet if tuple is new or not av _ [ Time, T2V[ LOOPHOLE[ ev.EventTime]], T2V[ LOOPHOLE[ ev.EventTime]]]; avl _ CONS[ av, avl]; av _ [ Duration, I2V[ ev.Duration], I2V[ ev.Duration]]; avl _ CONS[ av, avl]; av _ [ Text, textEntity, textEntity]; avl _ CONS[ av, avl]; av _ [ Message, messageEntity, messageEntity]; avl _ CONS[ av, avl]; av _ [ Place, placeEntity, placeEntity]; avl _ CONS[ av, avl]; av _ [ RepeatType, I2V[ HickorySupport.R2I[ ev.RepeatType]], I2V[ HickorySupport.R2I[ ev.RepeatType]]]; avl _ CONS[ av, avl]; relSet _ RelationSubset[ Rel, avl]; IF ( tuple _ NextRelship[ relSet] ) # NIL THEN BEGIN answer _ TRUE; key _ NameOf[ V2E[ GetF[ tuple, Key]]] END ELSE answer _ FALSE; ReleaseRelshipSet[ relSet]; RETURN[ answer, key]; END; -- DuplicateTuple EnterIcon: INTERNAL PROCEDURE [ label, flavor: Rope.ROPE] RETURNS [ icon: Entity] = BEGIN OPEN iconRel; tuple: Relship; av: AttributeValue; avl: AttributeValueList _ NIL; key: Entity; av _ [ IconLabel, label, label]; avl _ CONS[ av, avl]; av _ [ IconFlavor, flavor, flavor]; avl _ CONS[ av, avl]; tuple _ DeclareRelship[ Rel, avl, OldOnly]; -- see if such an icon already registered IF tuple = NIL THEN BEGIN -- this icon not yet registered key _ DeclareEntity[ iconDomain, NIL, NewOnly]; -- new entity av _ [ Key, key, key]; avl _ CONS[ av, avl]; tuple _ DeclareRelship[ Rel, avl, NewOnly]; END ELSE key _ V2E[ GetF[ tuple, Key]]; RETURN[ key]; END; -- EnterIcon END. ì/ivy/binding/hickory/hickoryStoreImpl.mesa storing events into the data base Last Edited by: Binding, August 16, 1984 1:37:05 pm PDT PDT The Signal definition Testing events on equality to test equality on some of the non key attributes Entering Events into Data Base constrain duration such that event starts and ends on same day. Reason is the way calendar deals with events.... here we find the event tuple and modify the attribute values in data base stores a new event as a new tuple into the data base to delete one tuple from repeatRel stores the repetition descriptor of an event in data base. Avoids duplicates. this is conservative, but we can still forget the event at any time. to check if the tuple already in data base. We avoid duplicate tuples in data base, based on some of the non-key attributes. Also return the key in case of a duplicate tuple. looks into icon relation to see if icon already entered and if not creates a new entry returning the entity of the old or new icon Ê M˜Jšœ*™*Jšœ!™!Jšœ;™;J˜šÏk ˜ Jšœ œœ8˜MJšœœõ˜ýJšœœ+˜8Jšœ œP˜bJšœ œ˜)Jšœœ˜%JšœœQ˜eJšœœ ˜!Jšœœœ˜Jšœ˜—J˜šœœ˜Jšœ˜Jšœœ\˜fJšœ˜Jšœ:˜@J˜—šœ œ&˜2J˜—šœ™J˜Jš Ïbœœœ)œœ˜LJ˜—šœ™J˜š Ïn œœœœ˜IJšœ˜JšœÏc ˜—J˜š Ÿœœ œœœ˜SJšœ2™2š œ"œ œ%œ&œ$œ˜âšœ!œ˜.šœ-œ˜:šœ#œ˜0šœ'œ˜4Jšœœ˜—————Jšœœ˜Jšœ ˜—J˜—šœ™J˜š Ÿ œœœ œ%œ˜oJšœœœ˜Jšœ œ˜J˜ J˜ J˜J˜Jš œœœœœ˜.Jšœ3œœœ˜WJšœQ™QJšœ™J˜(J˜/J˜CJšœœœœ˜7J˜9š œ œœœ œ˜,Jšœœœ˜J˜&Jšœ"œ$˜LJšœ ˜ J˜(šœœ˜J˜Jšœ7˜7šœœœ˜Jšœ1˜1Jšœ?˜?Jšœ˜—Jšœ˜—Jš˜—Jšœœ œœ˜'Jšœ˜ Jšœ  ˜J˜—š Ÿ œœœ œ˜GJšœI™IJšœœœ˜Jšœœ˜Jšœ œ˜J˜ Jšœ œœ˜J˜š œœœœœ˜.šœœ ˜Jšœ;œœ˜N—˜J˜"J˜8Jšœ œœœ ˜9Jš œœ#œœœ˜LJšœœœ˜-J˜;Jšœ œ2˜CJšœ" ˜2Jšœ-  ˜9J˜,šœœ ˜J˜J˜J˜J˜J˜-Jšœœ ˜;J˜Jšœ œœœ˜šœ#œ˜*Jšœœ˜5—šœ!˜'Jšœ.˜.—šœœ˜%J˜Jšœ7œ˜=Jšœ˜—šœœ˜%JšœF˜FJ˜Jš˜—Jšœ;˜?šœ!œ˜(Jšœ-˜-—šœ˜%Jšœ,˜,—šœ#œ˜*Jšœœ˜9—šœœ%œ˜0JšœE˜E—šœœ+œ˜6JšœO˜O—šœœ'˜1JšœH˜H—šœœ1œ2œ˜qJšœB˜B—JšœF˜FJšœ+˜+J˜+Jšœ œ˜Jšœ ˜——Jšœ ˜—Jšœ0œœ˜;Jšœ ˜J˜—š Ÿ œœ œœ"œ˜iJšœ4™4Jšœ ˜J˜Jšœ˜Jšœ ˜ J˜Jšœ œœ˜Jšœ œœ˜J˜šœœ ˜Jšœ;œœ˜OJ˜J˜"Jšœ(œ˜.šœ œ˜Jšœœœ˜!Jšœ˜—Jšœ%œ  ˜EJ˜Jšœœ ˜+Jšœ ˜ Jšœ˜Jšœ,˜,Jšœœ˜2Jšœ*˜*JšœC˜CJšœ*˜*Jšœ(˜(Jšœœ˜6JšœB˜BJšœL˜LJšœE˜EJšœ<˜J˜Jšœœ ˜J˜+Jš˜—Jšœ˜#Jšœ˜ Jšœ  ˜J˜—šœ˜J˜J˜J˜———…—*9I