/ivy/binding/hickory/hickoryStoreImpl.mesa
storing events into the data base
Last Edited by: Binding, August 16, 1984 1:37:05 pm PDT PDT
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;
The Signal definition
Error: PUBLIC SIGNAL [ errCode: Hickory.ErrorCode, arg: Rope.ROPE] = CODE;
Testing events on equality
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
to test equality on some of the non key attributes
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
Entering Events into Data Base
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];
constrain duration such that event starts and ends on same day. Reason is the way
calendar deals with events....
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: BOOLEANFALSE;
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
here we find the event tuple and modify the attribute values in data base
ENABLE UNWIND => NULL;
evEnt: DB.Entity;
duplicate: BOOLEAN;
key: Event;
success: BOOLEANFALSE;
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
stores a new event as a new tuple into the data base
OPEN eventRel;
tuple: Relship;
key: Event;
keyEnt: Entity;
duplicate: BOOLEANFALSE;
success: BOOLEANFALSE;
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
to delete one tuple from repeatRel
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
stores the repetition descriptor of an event in data base. Avoids duplicates.
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;
this is conservative, but we can still forget the event at any time.
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
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.
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
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
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.