-- /ivy/binding/hickory/hickoryGroupImpl.mesa
-- implementing the group features of events. Exporting routines to Hickory and HickoryGroup
-- Last edited by: Binding, July 25, 1984 4:27:30 pm PDT
DIRECTORY
DB USING [ AttributeValue, AttributeValueList, RelationSubset, NextRelship, ReleaseRelshipSet, DeclareEntity, DestroyEntity, Aborted, Relship, DestroyRelship, MarkTransaction, TransactionOf, Entity, DomainSubset, NextEntity, NameOf, RelshipSet, EntitySet, Version, ReleaseEntitySet, DeclareRelship, V2E, GetF],
Hickory USING [ Event, Group, GroupSet, Error, EventSet],
HickoryGroup,
HickoryNotify USING [ NotifyClients],
HickoryStorage USING [ protData],
HickorySupport USING [ StartTransaction, RestartTransaction],
RopeSets USING [ RopeSetEl, RopeSet, InsertValueIntoSet, DeleteValueFromSet, IsSetEmpty, IsValueInSet, Union]
;
HickoryGroupImpl: CEDAR MONITOR
LOCKS HickoryStorage.protData
IMPORTS HickoryStorage, Hickory, DB, HickorySupport, RopeSets, HickoryNotify
EXPORTS Hickory, HickoryGroup
SHARES HickorySupport, HickoryNotify
= BEGIN OPEN Hickory, HickoryGroup, DB, HickoryStorage.protData;
Global variables
groupCache: GroupSet;
Routines exported to Hickory
InsertToGroup: PUBLIC ENTRY PROCEDURE [ ev: Event, groups: GroupSet] = BEGIN
to insert the supplied event ( if valid) into group.
ENABLE BEGIN
UNWIND => NULL;
END; -- Enable
IF RopeSets.IsSetEmpty[ groups] THEN RETURN;
IF NOT owner THEN ERROR Error[ NotOwner, NIL];
InsertEventToGroup[ ev, groups];
HickoryNotify.NotifyClients[ InsertionToGroup, ev, groups];
END; -- InsertToGroup
RemoveFromGroup: PUBLIC ENTRY PROCEDURE [ ev: Event, groups: GroupSet] = BEGIN
to remove one entity from one or several groups.
OPEN groupRel;
ENABLE UNWIND => NULL;
evEnt, groupEnt: Entity;
av: AttributeValue;
avl: AttributeValueList ← NIL;
relSet: RelshipSet;
tuple: Relship;
group: REF RopeSets.RopeSetEl ← NIL;
success: BOOLEAN ← FALSE;
IF RopeSets.IsSetEmpty[ groups] THEN RETURN;
IF NOT owner THEN ERROR Error[ NotOwner, NIL];
WHILE NOT success DO
ENABLE Aborted => { HickorySupport.RestartTransaction; success ← FALSE; LOOP };
HickorySupport.StartTransaction[];
evEnt ← DeclareEntity[ eventDomain, ev, OldOnly];
IF evEnt = NIL THEN ERROR Hickory.Error[ NoSuchEvent, ev];
group ← groups.Head;
WHILE group # NIL DO
groupEnt ← DeclareEntity[ groupDomain, group^.Value, OldOnly];
IF groupEnt = NIL THEN ERROR Hickory.Error[ NoSuchGroup, group^.Value];
av ← [ attribute: Event, hi: evEnt, lo: evEnt];
avl ← CONS[ av, avl];
av ← [ attribute: Group, lo: groupEnt, hi: groupEnt];
avl ← CONS[ av, avl];
relSet ← RelationSubset[ Rel, avl];
tuple ← NextRelship[ relSet];
IF tuple # NIL THEN DestroyRelship[ tuple];
ReleaseRelshipSet[ relSet];
group ← group.Next;
ENDLOOP;
MarkTransaction[ TransactionOf[ $Hickory]];
success ← TRUE;
ENDLOOP;
HickoryNotify.NotifyClients[ RemoveFromGroup, ev, groups];
END; -- RemoveFromGroup
RemoveGroup: PUBLIC ENTRY PROCEDURE [ group: Group] = BEGIN
to destroy a group. Events are not removed from event relation,
only destroyed within group relation.
OPEN groupRel;
ENABLE UNWIND => NULL;
groupEnt: Entity;
success: BOOLEAN ← FALSE;
IF NOT owner THEN ERROR Error[ NotOwner, NIL];
WHILE NOT success DO
ENABLE Aborted => { HickorySupport.RestartTransaction; success ← FALSE; LOOP };
HickorySupport.StartTransaction[];
groupEnt ← DeclareEntity[ groupDomain, group, OldOnly];
IF groupEnt = NIL THEN ERROR Hickory.Error[ NoSuchGroup, group];
DestroyEntity[ groupEnt]; -- should destroy all entries in group relation...
groupCache ← RopeSets.DeleteValueFromSet[ group, groupCache];
MarkTransaction[ TransactionOf[ $Hickory]];
success ← TRUE;
ENDLOOP; -- Transaction
HickoryNotify.NotifyClients[ GroupDestroy, NIL, RopeSets.InsertValueIntoSet[ group, [NIL, NIL]]];
END; -- RemoveGroup
ListGroups: PUBLIC ENTRY PROCEDURE [ ev: Hickory.Event ← NIL] RETURNS [ gSet: GroupSet] = BEGIN
enumerates all groups, returns ordered list
ENABLE UNWIND => NULL;
entSet: EntitySet;
ent: Entity;
success: BOOLEANFALSE;
gSet.Head ← NIL; gSet.Tail ← NIL;
IF ev = NIL THEN BEGIN
IF RopeSets.IsSetEmpty[ groupCache] THEN
WHILE NOT success DO
ENABLE Aborted => { HickorySupport.RestartTransaction; success ← FALSE; LOOP };
HickorySupport.StartTransaction[];
entSet ← DomainSubset[ d: groupDomain];
WHILE ( ent ← NextEntity[ entSet]) # NIL DO
group: Group ← NameOf[ ent];
groupCache ← RopeSets.InsertValueIntoSet[ group, groupCache];
ENDLOOP;
ReleaseEntitySet[ entSet];
MarkTransaction[ TransactionOf[ $Hickory]];
success ← TRUE;
ENDLOOP;
gSet ← RopeSets.Union[ groupCache, [ NIL, NIL]]; -- make a copy
END
ELSE BEGIN -- ev # NIL
OPEN groupRel;
av: AttributeValue;
relSet: RelshipSet;
tuple: Relship;
evEnt: Entity;
WHILE NOT success DO
ENABLE Aborted => { HickorySupport.RestartTransaction; success ← FALSE; LOOP };
HickorySupport.StartTransaction[];
evEnt ← DeclareEntity[ eventDomain, ev, OldOnly];
IF evEnt = NIL THEN ERROR Error[ NoSuchEvent, ev];
av ← [ attribute: Event, lo: evEnt, hi: evEnt];
relSet ← RelationSubset[ Rel, LIST[ av]];
WHILE ( tuple ← NextRelship[ relSet]) # NIL DO
gSet ← RopeSets.InsertValueIntoSet[ NameOf[ V2E[ GetF[ tuple, Group]]], gSet];
ENDLOOP;
ReleaseRelshipSet[ relSet];
MarkTransaction[ TransactionOf[ $Hickory]];
success ← TRUE;
ENDLOOP;
END; -- IF ev # NIL
RETURN[ gSet];
END; -- ListGroups
The routines exported to HickoryGroup, all internal to monitor
InsertEventToGroup: PUBLIC INTERNAL PROCEDURE [ ev: Event, groups: GroupSet] = BEGIN
to insert the event entity into group, creating one if needed
OPEN groupRel;
groupEnt: Entity;
av: AttributeValue;
avl: AttributeValueList ← NIL;
evEnt: Entity;
group: REF RopeSets.RopeSetEl ← groups.Head;
success: BOOLEANFALSE;
WHILE NOT success DO
ENABLE Aborted => { HickorySupport.RestartTransaction; success ← FALSE; LOOP};
HickorySupport.StartTransaction[];
evEnt ← DeclareEntity[ eventDomain, ev, OldOnly];
IF evEnt = NIL THEN ERROR Hickory.Error[ NoSuchEvent, ev];
WHILE group # NIL DO
groupEnt ← DeclareEntity[ groupDomain, group^.Value, OldOnly];
IF groupEnt = NIL THEN BEGIN
groupEnt ← DeclareEntity[ groupDomain, group^.Value, NewOnly];
HickoryNotify.NotifyClients[ NewGroup, NIL, RopeSets.InsertValueIntoSet[ group^.Value, [NIL, NIL]]];
END;
groupCache ← RopeSets.InsertValueIntoSet[ group^.Value, groupCache];
av ← [ attribute: Event, lo: evEnt, hi: evEnt];
avl ← CONS[ av, avl];
av ← [ attribute: Group, lo: groupEnt, hi: groupEnt];
avl ← CONS[ av, avl];
[] ← DeclareRelship[ Rel, avl, NewOrOld];
group ← group.Next;
ENDLOOP;
MarkTransaction[ TransactionOf[ $Hickory]];
success ← TRUE;
ENDLOOP; -- Transaction
END; -- InsertEventToGroup
GetGroupEvents: PUBLIC INTERNAL PROCEDURE [ group: Group] RETURNS [ eventsOfGroup: EventSet] = BEGIN
enumerates in an ordered list all events in a group.
OPEN groupRel;
relSet: RelshipSet;
tuple: Relship;
av: AttributeValue;
groupEnt: Entity;
success: BOOLEANFALSE;
IF group = NIL THEN RETURN[ [NIL, NIL]];
eventsOfGroup.Head ← NIL; eventsOfGroup.Tail ← NIL;
WHILE NOT success DO
ENABLE Aborted => { HickorySupport.RestartTransaction; success ← FALSE; LOOP};
HickorySupport.StartTransaction[];
groupEnt ← DeclareEntity[ groupDomain, group, OldOnly];
IF groupEnt = NIL THEN ERROR Hickory.Error[ NoSuchGroup, group];
av ← [ attribute: Group, lo: groupEnt, hi: groupEnt];
relSet ← RelationSubset[ Rel, LIST[ av]];
WHILE ( tuple ← NextRelship[ relSet]) # NIL DO
eventsOfGroup ← RopeSets.InsertValueIntoSet[ NameOf[ V2E[ GetF[ tuple, Event]]] , eventsOfGroup];
ENDLOOP;
ReleaseRelshipSet[ relSet];
MarkTransaction[ TransactionOf[ $Hickory]];
success ← TRUE;
ENDLOOP; -- Transaction
RETURN[ eventsOfGroup];
END; -- GetGroupEvents
DeclareGroups: PUBLIC INTERNAL PROCEDURE [ groups: LIST OF Group] = BEGIN
to declare a number of groups at initialization. Groups are read from user profile
Is also called whenever user profile was edited.., but we check whether or not we
really have new groups!
newGroups: RopeSets.RopeSet;
IF groups = NIL THEN RETURN;
FOR l: LIST OF Group ← groups, l.rest UNTIL l = NIL DO
IF NOT RopeSets.IsValueInSet[ l.first, groupCache] THEN
newGroups ← RopeSets.InsertValueIntoSet[ l.first, newGroups];
ENDLOOP;
IF RopeSets.IsSetEmpty[ newGroups] THEN RETURN; -- don't bother to access DB
FOR l: REF RopeSets.RopeSetEl ← newGroups.Head, l.Next UNTIL l = NIL DO
[] ← DeclareEntity[ groupDomain, l.Value, NewOrOld];
groupCache ← RopeSets.InsertValueIntoSet[ l.Value, groupCache];
ENDLOOP;
END;
InvalidateCache: PUBLIC INTERNAL PROCEDURE = BEGIN
groupCache.Head ← NIL; groupCache.Tail ← NIL;
END; -- InvalidateCache
InitializeCache: PUBLIC INTERNAL PROCEDURE = BEGIN
to initialize the cache of Hickory.Group
entSet: EntitySet;
ent: Entity;
groupCache.Head ← NIL; groupCache.Tail ← NIL;
entSet ← DomainSubset[ d: groupDomain];
WHILE ( ent ← NextEntity[ entSet]) # NIL DO
group: Group ← NameOf[ ent];
groupCache ← RopeSets.InsertValueIntoSet[ group, groupCache];
ENDLOOP;
ReleaseEntitySet[ entSet];
END;
Initially
groupCache.Head ← NIL; groupCache.Tail ← NIL;
END.