DIRECTORY
BasicTime USING [ GMT, nullGMT, earliestGMT, Period],
DB USING [ AttributeValue, AttributeValueList, Relship, RelshipSet, RelationSubset, NextRelship, ReleaseRelshipSet, Entity, Aborted, T2V, I2V, DeclareEntity, MarkTransaction, TransactionOf, V2B, GetF, NameOf, V2E, V2T, V2I, V2S],
Hickory USING [ EventTuple, Event, EventSet, EventList, Error, RepetitionType, All, Reminders, ExpandRepetitions, Group, ProtectionType, Options, NoOptions, MergeLists, OrderEventList],
HickoryCache USING [ FindNextOccurrence, MaintainCache, MaxCost, FindCachedEvent],
HickoryGroup USING [ GetGroupEvents],
HickorySupport USING [ StartTransaction, RestartTransaction, I2P, I2R, R2I, ReConvertRopes, GetNextOccurrence],
HickoryStorage USING [ protData],
Rope USING [ ROPE],
RopeSets USING [ IsSetEmpty, IsValueInSet]
;
Querying the data base
RestoreEvents:
PUBLIC
ENTRY
PROCEDURE [ from, to: BasicTime.
GMT, options: Options ← NoOptions, group: Group ←
NIL, key: Event ←
NIL]
RETURNS [ evList:
LIST
OF EventTuple] =
BEGIN
returns the event tuples falling in the given time interval [ from..to].
This includes the repeated occurrences of a repeated event that fall into the interval
if 'options' include 'ExpandRepetition'. Otherwise, only the first occurrence of a
repeated event which falls into the given time interval is returned.
If 'All' is set in 'options', also return the events that have been "forgotten".
If group # NIL and key = NIL, return only events that are in the specified group.
If key # NIL, then return a list that either includes only one element
or the expanded repetions if options are set properly. The 'group' argument is
ignored if key # NIL.
If it is not the owner of the data base who is accessing it, some fields of the
returned tuples are blanked out...( Text, Message, Place)
raises Error[ NullInterval], Error[ BadInterval], Error[ NoSuchEvent]
ENABLE UNWIND => NULL;
simpleEvl: EventList ← NIL;
repeatedEvl: EventList ← NIL;
eventsOfGroup: Hickory.EventSet;
keyEnt: DB.Entity ← NIL;
success: BOOLEAN ← FALSE;
IF BasicTime.Period[ from, to] < 0 THEN ERROR Hickory.Error[ BadInterval, NIL]
ELSE IF from = to THEN ERROR Hickory.Error[ NullInterval, NIL];
eventsOfGroup.Head ← NIL; eventsOfGroup.Tail ← NIL;
evList ← NIL;
IF key #
NIL
AND group =
NIL
THEN
BEGIN
found: BOOLEAN ← FALSE;
[ found, evList] ← LookInEnteredEventsCache[ key, options, from, to];
IF found THEN RETURN[ evList];
END;
WHILE
NOT success
DO
ENABLE Aborted => { HickorySupport.RestartTransaction; success ← FALSE; LOOP };
HickorySupport.StartTransaction[];
IF key #
NIL
THEN
BEGIN
keyEnt ← DeclareEntity[ eventDomain, key, OldOnly];
IF keyEnt = NIL THEN ERROR Error[ NoSuchEvent, key];
END
ELSE
IF group #
NIL
THEN
BEGIN
eventsOfGroup ← HickoryGroup.GetGroupEvents[ group];
IF RopeSets.IsSetEmpty[ eventsOfGroup] THEN RETURN[ NIL]; -- empty group
END;
simpleEvl ← GetEvents[ from, to, options, eventsOfGroup, keyEnt, TRUE];
repeatedEvl ← GetEvents[ from, to, options, eventsOfGroup, keyEnt, FALSE];
MarkTransaction[ TransactionOf[ $Hickory]];
success ← TRUE;
ENDLOOP;
evList ← MergeLists[ simpleEvl, repeatedEvl];
RETURN[ evList];
END; --RestoreEvents
LookInEnteredEventsCache:
INTERNAL
PROCEDURE [ key: Event, options: Options, from, to: BasicTime.
GMT]
RETURNS [ found:
BOOLEAN, evl: EventList] =
BEGIN
l: EventList ← HickoryCache.FindCachedEvent[ key];
IF l = NIL THEN RETURN[ FALSE, NIL];
IF options[ Reminders] AND NOT l.first.Remind THEN RETURN[ TRUE, NIL];
IF NOT options[ All] AND l.first.Forgotten THEN RETURN[ TRUE, NIL];
IF NOT( BasicTime.Period[ from, l.first.EventTime] >= 0 AND BasicTime.Period[ l.first.EventTime, to] >= 0) THEN RETURN[ TRUE, NIL];
IF l.first.RepeatType = None OR NOT options[ ExpandRepetitions] THEN RETURN[ TRUE, l]
ELSE
BEGIN
until: BasicTime.GMT ← MinTime[ to, l.first.RepeatUntil];
evl ← Expand[ l.first, until];
evl ← OrderEventList[ evl];
RETURN[ TRUE, evl];
END;
END;
-- LookInEnteredEventsCache
BuildQueryConstraints:
INTERNAL
PROCEDURE [ simple:
BOOLEAN, options: Options, from, to: BasicTime.
GMT, evSet: Hickory.EventSet, keyEnt: Entity ←
NIL]
RETURNS [ avl: AttributeValueList ←
NIL] =
BEGIN
to figure out the constraints on a query
OPEN eventRel;
av: AttributeValue;
forget: BOOLEAN ← NOT options[ All];
reminders: BOOLEAN ← options[ Reminders];
IF forget
THEN
BEGIN
-- don't include forgotten events
av ← [ attribute: Forgotten, hi: B2V[ FALSE], lo: B2V[ FALSE]];
avl ← CONS[ av, avl]; *****DB. DBModelPrivateImpl.NCode throws up...!
NULL;
END;
IF reminders
THEN
BEGIN
-- include only events to be reminded
av ← [ attribute: Reminder, hi: B2V[ TRUE], lo: B2V[ TRUE]];
avl ← CONS[ av, avl]; *****DB. DBModelPrivateImpl.NCode throws up...!
NULL;
END;
IF simple THEN av ← [ Time, T2V[ LOOPHOLE[ from]], T2V[ LOOPHOLE[ to]]]
ELSE av ← [ Time, T2V[ LOOPHOLE[ BasicTime.earliestGMT]], T2V[ LOOPHOLE[ to]]];
avl ← CONS[ av, avl];
IF simple THEN av ← [ RepeatType, I2V[ HickorySupport.R2I[ None]], I2V[ HickorySupport.R2I[ None]]]
ELSE av ← [ attribute: RepeatType, lo: I2V[ HickorySupport.R2I[ Hourly]], hi: I2V[ HickorySupport.R2I[ Complicated]]];
avl ← CONS[ av, avl];
IF keyEnt #
NIL
THEN
BEGIN
av ← [ attribute: Key, lo: keyEnt, hi: keyEnt];
avl ← CONS[ av, avl];
END
ELSE
IF
NOT RopeSets.IsSetEmpty[ evSet]
THEN
BEGIN
-- it's a group query...
we can restrict query to largest and smallest event entity in that group!
headEnt, tailEnt: Entity;
headEnt ← DeclareEntity[ eventDomain, evSet.Head.Value, OldOnly];
tailEnt ← DeclareEntity[ eventDomain, evSet.Tail.Value, OldOnly];
IF headEnt = NIL THEN ERROR Hickory.Error[ NoSuchEvent, evSet.Head.Value];
IF tailEnt = NIL THEN ERROR Hickory.Error[ NoSuchEvent, evSet.Tail.Value ];
av ← [ attribute: Key, lo: headEnt, hi: tailEnt];
avl ← CONS[ av, avl];
END;
RETURN[ avl];
END; -- BuildQueryConstraints
GetEvents:
INTERNAL
PROCEDURE [ from, to: BasicTime.
GMT, options: Options, es: Hickory.EventSet, keyEnt: Entity, simple:
BOOLEAN]
RETURNS [ evl: EventList] =
BEGIN
returns all the events in time interval. 'to' inclusive..!
OPEN eventRel;
avl: AttributeValueList;
relSet: RelshipSet;
expand: BOOLEAN ← options[ ExpandRepetitions];
avl ← BuildQueryConstraints[ simple, options, from, to, es, keyEnt];
relSet ← RelationSubset[ Rel, avl];
IF simple THEN evl ← BuildSimpleEventList[ relSet, options, es]
ELSE evl ← BuildRepeatedEventList[ relSet, from, to, options, es];
ReleaseRelshipSet[ relSet];
IF
NOT simple
THEN
BEGIN
-- for repeated events
evl ← OrderEventList[ evl]; -- must reorder on EventTime
IF expand THEN evl ← ExpandRepeatedEventList[ evl, to];
END;
END; -- GetEvents
BuildSimpleEventList:
INTERNAL
PROCEDURE [ relSet: RelshipSet, options: Options, es: Hickory.EventSet]
RETURNS [ evList:
LIST
OF EventTuple] =
BEGIN
to convert from a RelshipSet into a list of EventTuples Assume all events have RepetionType
= None...
OPEN eventRel;
key: Event;
msg, txt, pl, iconFlav, iconLab: Rope.ROPE;
prot: Hickory.ProtectionType;
tuple: Relship;
ev: EventTuple;
head, tail, new: EventList;
reminders: BOOLEAN ← options[ Reminders];
forget: BOOLEAN ← NOT options[ All];
head ← NIL; tail ← NIL; new ← NIL;
WHILE ( tuple ← NextRelship[ relSet]) #
NIL
DO
forgotten: BOOLEAN ← V2B[ GetF[ tuple, Forgotten]];
reminder: BOOLEAN ← V2B[ GetF[ tuple, Reminder]];
IF forget AND forgotten THEN LOOP;
IF NOT reminder AND reminders THEN LOOP;
key ← NameOf[ V2E[ GetF[ tuple, Key]]];
IF NOT RopeSets.IsSetEmpty[ es] AND NOT RopeSets.IsValueInSet[ key, es] THEN LOOP;
[ iconFlav, iconLab] ← QueryIcons[ V2E[ GetF[ tuple, Icon]]];
prot ← HickorySupport.I2P[ V2I[ GetF[ tuple, Protection]]];
IF prot = Private
AND
NOT owner
THEN
BEGIN
msg ← ""; txt ← ""; pl ← "";
END
ELSE
BEGIN
msg ← NameOf[ V2E[ GetF[ tuple, Message]]];
txt ← NameOf[ V2E[ GetF[ tuple, Text]]];
pl ← NameOf[ V2E[ GetF[ tuple, Place]]];
END;
ev ← [ Key: key,
EventTime: LOOPHOLE[ V2T[ GetF[ tuple, Time]]],
Duration: V2I[ GetF[ tuple, Duration]],
RepeatType: None,
RepeatTime: NIL,
RepeatUntil: BasicTime.nullGMT,
KeepUntil: LOOPHOLE[ V2T[ GetF[ tuple, KeepTime]]],
LeadTime: V2I[ GetF[ tuple, LeadTime]],
NagTime: V2I[ GetF[ tuple, NagTime]],
IconFlavor: iconFlav,
IconLabel: iconLab,
Message: msg,
Text: txt,
Place: pl,
Protection: prot,
Forgotten: forgotten,
Remind: reminder];
ev ← HickorySupport.ReConvertRopes[ ev]; -- "" -> NIL!
new ← CONS[ ev, NIL];
IF head =
NIL
THEN
BEGIN
-- pretty painful to keep things ordered
head ← new; tail ← head;
END
ELSE
BEGIN
tail.rest ← new;
tail ← new;
END;
ENDLOOP;
I do not know why things are not orderd on EventTime: DB must be the culprit?
head ← OrderEventList[ head]; -- this should not be needed if DB returned things ordered!
RETURN[ head];
END; -- BuildSimpleEventList
BuildRepeatedEventList:
INTERNAL
PROCEDURE [ relSet: RelshipSet, from, to: BasicTime.GMT, options: Options, es: Hickory.EventSet]
RETURNS [ evList: EventList] =
BEGIN
to convert from a RelshipSet into a list of EventTuples Assume all events have
RepetionType # None... The returned list is NOT ORDERED.
OPEN eventRel;
key: Event;
msg, txt, pl, iconFlav, iconLab, repTime: Rope.ROPE;
prot: Hickory.ProtectionType;
tuple: Relship;
ev: EventTuple;
evEnt: Entity;
lastTime, firstTime, next, repeatUntil: BasicTime.GMT;
repType: RepetitionType;
forget: BOOLEAN ← NOT options[ All];
reminders: BOOLEAN ← options[ Reminders];
evList ← NIL;
-- relSet contains ALL repeated events, even the forgotten ones for now!
WHILE ( tuple ← NextRelship[ relSet]) #
NIL
DO
forgotten: BOOLEAN ← V2B[ GetF[ tuple, Forgotten]];
reminder: BOOLEAN ← V2B[ GetF[ tuple, Reminder]];
IF forget AND forgotten THEN LOOP;
IF NOT reminder AND reminders THEN LOOP;
evEnt ← V2E[ GetF[ tuple, Key]];
[ firstTime, lastTime, repType, repTime, repeatUntil] ← GetRepeatPars[ evEnt];
IF BasicTime.Period[ from, repeatUntil] >= 0
THEN
BEGIN
must take a closer look
key ← NameOf[ evEnt];
IF NOT RopeSets.IsSetEmpty[ es] AND NOT RopeSets.IsValueInSet[ key, es] THEN LOOP;
assumption: lastTime is the closest repeated occurrence before the present...
firstTime was the very first time it ever happened.
next ← HickoryCache.FindNextOccurrence[ key, firstTime, lastTime, from, repType, repTime];
next >= from...
IF BasicTime.Period[ to, next] <= 0
THEN
BEGIN
-- there is occurrence in interval...
[ iconFlav, iconLab] ← QueryIcons[ V2E[ GetF[ tuple, Icon]]];
prot ← HickorySupport.I2P[ V2I[ GetF[ tuple, Protection]]];
IF prot = Private
AND
NOT owner
THEN
BEGIN
msg ← ""; txt ← ""; pl ← "";
END
ELSE
BEGIN
msg ← NameOf[ V2E[ GetF[ tuple, Message]]];
txt ← NameOf[ V2E[ GetF[ tuple, Text]]];
pl ← NameOf[ V2E[ GetF[ tuple, Place]]];
END;
ev ← [ Key: key,
EventTime: next,
Duration: V2I[ GetF[ tuple, Duration]],
RepeatType: repType,
RepeatTime: repTime,
RepeatUntil: repeatUntil,
KeepUntil: LOOPHOLE[ V2T[ GetF[ tuple, KeepTime]]],
LeadTime: V2I[ GetF[ tuple, LeadTime]],
NagTime: V2I[ GetF[ tuple, NagTime]],
IconFlavor: iconFlav,
IconLabel: iconLab,
Message: msg,
Text: txt,
Place: pl,
Protection: prot,
Remind: reminder,
Forgotten: forgotten];
ev ← HickorySupport.ReConvertRopes[ ev];
evList ← CONS[ ev, evList]; -- order within list not relevant here
END; -- if next > to...
END; -- if repeatUntil < from...
ENDLOOP;
RETURN[ evList];
END; -- BuildRepeatedEventList
GetRepeatPars:
INTERNAL
PROCEDURE [ evEnt: Entity]
RETURNS [ firstTime, lastTime: BasicTime.
GMT, repType: RepetitionType, repTime: Rope.
ROPE, repUntil: BasicTime.
GMT] =
BEGIN
to get hold of the repetiton parameters
OPEN repeatRel;
relSet: RelshipSet;
tuple: Relship;
av: AttributeValue;
av ← [ Event, evEnt, evEnt];
relSet ← RelationSubset[ Rel, LIST[ av]];
tuple ← NextRelship[ relSet];
IF tuple = NIL THEN ERROR;
firstTime ← LOOPHOLE[ V2T[ GetF[ tuple, FirstTime]]];
lastTime ← LOOPHOLE[ V2T[ GetF[ tuple, LastOccurred]]];
repTime ← NameOf[ V2E[ GetF[ tuple, RepeatTime]]];
repType ← HickorySupport.I2R[ V2I[ GetF[ tuple, RepeatType]]];
repUntil ← LOOPHOLE[ V2T[ GetF[ tuple, RepeatUntil]]];
IF NextRelship[ relSet] # NIL THEN ERROR;
ReleaseRelshipSet[ relSet];
RETURN[ firstTime, lastTime, repType, repTime, repUntil];
END; -- GetRepeatPar
ExpandRepeatedEventList:
INTERNAL
PROCEDURE [ evl: EventList, to: BasicTime.
GMT]
RETURNS [ newList: EventList] =
BEGIN
insert repeated occurrences for the repeated events that happen before 'to'
tempList: EventList;
newList ← NIL;
FOR l: EventList ← evl, l.rest
UNTIL l =
NIL
DO
tempList ← Expand[ l.first, to];
newList ← MergeLists[ newList, tempList];
ENDLOOP;
RETURN[ newList];
END;
-- ExpandRepeatedEventList
Expand:
INTERNAL
PROCEDURE[ ev: EventTuple, to: BasicTime.
GMT]
RETURNS [ evl: EventList] =
BEGIN
make a list for all repetitions that happen until 'to'. Don't use the cache since
generation of times is purely sequential..., so it is not worth the bother. Do however
insert generated occurrences into cache
tail, new: EventList;
nextTime: BasicTime.GMT;
cost: CARDINAL ← 0;
evl ← CONS[ ev, NIL];
tail ← evl;
DO
-- ..EventTime is for sure in time interval of query
nextTime ← HickorySupport.GetNextOccurrence[ LOOPHOLE[ tail.first.EventTime], tail.first.RepeatType, tail.first.RepeatTime];
IF cost >= HickoryCache.MaxCost
THEN
BEGIN
HickoryCache.MaintainCache[ ev.Key, nextTime];
cost ← 0;
END
ELSE cost ← cost + 1;
IF BasicTime.Period[ to, nextTime] > 0 THEN EXIT;
IF BasicTime.Period[ ev.RepeatUntil, nextTime] > 0 THEN EXIT;
new ← CONS[ tail.first, NIL];
new.first.EventTime ← nextTime;
tail.rest ← new;
tail ← new;
ENDLOOP;
RETURN[ evl];
END; -- Expand
QueryIcons:
INTERNAL
PROCEDURE [ key: Entity]
RETURNS [ iconFlav, iconLab: Rope.
ROPE] =
BEGIN
given the key we retrieve the associated label and flavor attribute in icon relation
OPEN iconRel;
av: AttributeValue;
relSet: RelshipSet;
tuple: Relship;
av ← [ Key, key, key];
relSet ← RelationSubset[ Rel, LIST[ av]];
tuple ← NextRelship[ relSet];
IF tuple = NIL THEN RETURN[ "", ""];
iconFlav ← V2S[ GetF[ tuple, IconFlavor]];
iconLab ← V2S[ GetF[ tuple, IconLabel]];
ReleaseRelshipSet[ relSet];
RETURN[ iconFlav, iconLab];
END; -- QueryIcons
END.