/ivy/binding/hickory/hickoryPropImpl.mesa
implementing the properties for events
The implementation of properties is rather akward. For mainly tow reasons. First, Cypress did
not allow me to store entities of type AttributeDomain in a relation. That is the reason why
I did not use SetP, GetP since they require the knowledge of the "aIs" attribute when
querying. The next trial was having a single property relation that has an attribute of
domain "AnyDomainType", but this does not work out either. The reason is that Cypress
checks the type of the value ( ref int, ref bool,...) even if the attribute is declared to be of
AnyDomain. It then rises the ERROR MismatchedAttributeType and does not store the value,
so we can't simply RESUME ....! Hence, the rather large number of property relations
Last edited by: Binding, August 1, 1984 8:17:44 am PDT
DIRECTORY
BasicTime USING [ GMT],
DB USING[ Entity, Attribute, Aborted, Value, Version, DeclareEntity, Eq, GMT, B2V, I2V, T2V, S2V, MarkTransaction, TransactionOf, Relship, DeclareRelship, SetF, GetF, RelshipSet, RelationSubset, NextRelship, V2E, ReleaseRelshipSet, V2I, V2B, V2S, V2T, EntitySet, DomainSubset, NextEntity, ReleaseEntitySet, AttributeValue, AttributeValueList, NameOf, DestroyEntity],
Hickory USING [ Event, Property, Error, PropertySet],
HickoryNotify USING [ NotifyClients],
HickoryProp,
HickoryStorage USING [ protData, PropertyRel, PropertyTypes],
HickorySupport USING [ RestartTransaction, StartTransaction],
Rope USING [ ROPE],
RopeSets USING [ InsertValueIntoSet, DeleteValueFromSet, IsSetEmpty, Union]
;
HickoryPropImpl: CEDAR MONITOR
LOCKS HickoryStorage.protData
IMPORTS HickoryStorage, HickorySupport, DB, Hickory, RopeSets, HickoryNotify
EXPORTS Hickory, HickoryProp
SHARES HickorySupport, HickoryNotify
= BEGIN OPEN DB, Hickory, HickoryStorage.protData;
-- Global variables
propertyCache: PropertySet;
Action routines dealing with properties, all public entry points
SetProperty: PUBLIC ENTRY PROCEDURE [ ev: Event, name: Property, value: REF ANY] = BEGIN
to set the named property of the given event to the value.
ENABLE UNWIND => NULL;
MyRope: TYPE = Rope.ROPE;
type: Entity ← GetType[ value];
propEnt: Entity;
evEnt: Entity;
propType: Entity;
val: Value;
success: BOOLEANFALSE;
IF NOT owner THEN ERROR Error[ NotOwner, NIL];
IF value = NIL THEN ERROR Hickory.Error[ BadValue, NIL];
WHILE NOT success DO
ENABLE Aborted => { HickorySupport.RestartTransaction; success ← FALSE; LOOP };
HickorySupport.StartTransaction[];
propEnt ← DeclareEntity[ propertyDomain, name, OldOnly];
IF propEnt = NIL THEN propEnt ← DeclareAProperty[ name, type];
evEnt ← DeclareEntity [ eventDomain, ev, OldOnly];
IF evEnt = NIL THEN ERROR Hickory.Error[ NoSuchEvent, ev];
propType ← GetPropertyDescriptor[ propEnt];
IF NOT Eq[ type, propType] THEN ERROR Hickory.Error[ MismatchedPropertyType, NIL];
SELECT TRUE FROM
Eq[ type, IntegerEntity] => BEGIN
i: REF INTEGERNARROW[ value];
val ← I2V[ i^];
StoreAValue[ val, Int, evEnt, propEnt];
END; -- IntegerEntity
Eq[ type, BooleanEntity] => BEGIN
b: REF BOOLEANNARROW[ value];
val ← B2V[ b^];
StoreAValue[ val, Bool, evEnt, propEnt];
END; -- BooleanEntity
Eq[ type, TimeEntity] => BEGIN
time: REF GMTNARROW[ value]; -- REF BasicTime.GMT!
val ← T2V[ LOOPHOLE[ time]];
StoreAValue[ val, Time, evEnt, propEnt];
END; -- TimeEntity
Eq[ type, RopeEntity] => BEGIN
r: REF MyRope ← NARROW[ value];
val ← S2V[ r^];
StoreAValue[ val, String, evEnt, propEnt];
END; -- RopeEntity
ENDCASE;
MarkTransaction[ TransactionOf[ $Hickory]];
success ← TRUE;
ENDLOOP; -- Transaction
HickoryNotify.NotifyClients[ AddProperty, ev, RopeSets.InsertValueIntoSet[ name, [ NIL, NIL]]];
END; -- SetProperty
StoreAValue: INTERNAL PROCEDURE [ val: Value, where: HickoryStorage.PropertyTypes, evEnt, propEnt: Entity] = BEGIN
to store the supplied value in the appropriate property relation
OPEN propertyRels[ where];
av: AttributeValue;
avl: AttributeValueList ← NIL;
tuple: Relship;
av ← [ attribute: Event, hi: evEnt, lo: evEnt];
avl ← CONS[ av, avl];
av ← [ attribute: Property, hi: propEnt, lo: propEnt];
avl ← CONS[ av, avl];
tuple ← DeclareRelship[ Rel, avl]; -- to avoid duplicates...
SetF[ tuple, PropValue, val];
END; -- StoreAValue
GetType: INTERNAL PROCEDURE [ val: REF ANY] RETURNS [ type: Entity] = BEGIN
to find out the type of the value
IF ISTYPE[ val, REF INTEGER] THEN RETURN[ IntegerEntity];
IF ISTYPE[ val, REF BOOLEAN] THEN RETURN[ BooleanEntity];
IF ISTYPE[ val, REF GMT] THEN RETURN[ TimeEntity];
IF ISTYPE[ val, REF Rope.ROPE] THEN RETURN[ RopeEntity];
ERROR Hickory.Error[ BadValueType, NIL];
END; -- GetType
DeclareAProperty: INTERNAL PROCEDURE [ name: Property, type: Entity] RETURNS [ pEnt: Entity] = BEGIN
makes an entry into the propertyDescriptorRel after declaring a new property
in propertyDomain
OPEN propertyDescriptorRel;
propEnt: Entity ← DeclareEntity[ propertyDomain, name, NewOnly];
tuple: Relship ← DeclareRelship[ Rel]; -- in propertyDescriptorRel
propertyCache ← RopeSets.InsertValueIntoSet[ name, propertyCache];
SetF[ tuple, Property, propEnt];
SetF[ tuple, Type, type];
MarkTransaction[ TransactionOf[ $Hickory]];
HickoryNotify.NotifyClients[ NewProperty, NIL, RopeSets.InsertValueIntoSet[ name, [ NIL, NIL]]];
RETURN[ propEnt];
END; -- DeclareAProperty
GetPropertyDescriptor: INTERNAL PROCEDURE [ propEnt: Entity] RETURNS [ type: Entity] = BEGIN
to find the type of a property
OPEN propertyDescriptorRel;
relSet: RelshipSet;
av: AttributeValue;
tuple: Relship;
av ← [ attribute: Property, lo: propEnt, hi: propEnt];
relSet ← RelationSubset[ Rel, LIST[ av]];
tuple ← NextRelship[ relSet];
IF tuple = NIL THEN ERROR;
type ← V2E[ GetF[ tuple, Type]];
ReleaseRelshipSet[ relSet];
RETURN[ type];
END; -- GetPropertyDescriptor
GetProperty: PUBLIC ENTRY PROCEDURE [ ev: Event, name: Property] RETURNS [ value: REF ANY] = BEGIN
to find the property value of the event.
ENABLE UNWIND => NULL;
evEnt, propEnt: Entity;
val: Value;
type: Entity;
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];
propEnt ← DeclareEntity[ propertyDomain, name, OldOnly];
IF propEnt = NIL THEN ERROR Hickory.Error[ NoSuchProperty, name];
type ← GetPropertyDescriptor[ propEnt];
SELECT TRUE FROM
Eq[ type, BooleanEntity] => BEGIN
b: BOOLEAN;
val ← GetAValue[ evEnt, Bool];
IF val = NIL THEN RETURN[ NIL];
b ← V2B[ val];
value ← NEW[ BOOLEAN ← b];
END; -- Boolean
Eq[ type, IntegerEntity] => BEGIN
i: INTEGER;
val ← GetAValue[ evEnt, Int];
IF val = NIL THEN RETURN[ NIL];
i ← V2I[ val];
value ← NEW[ INTEGER ← i];
END ;-- integer
Eq[ type, RopeEntity] => BEGIN
r: Rope.ROPE;
val ← GetAValue[ evEnt, String];
IF val = NIL THEN RETURN[ NIL];
r ← V2S[ val];
value ← NEW[ Rope.ROPE ← r];
END; -- Rope
Eq[ type, TimeEntity] => BEGIN
t: BasicTime.GMT;
val ← GetAValue[ evEnt, Time];
IF val = NIL THEN RETURN[ NIL];
t ← LOOPHOLE[ V2T[ val]];
value ← NEW[ BasicTime.GMT ← t];
END; -- Time
ENDCASE;
MarkTransaction[ TransactionOf[ $Hickory]];
success ← TRUE;
ENDLOOP;
RETURN[ value];
END; -- GetProperty
GetAValue: INTERNAL PROCEDURE [ evEnt: Entity, where: HickoryStorage.PropertyTypes] RETURNS [ val: Value] = BEGIN
to retrieve the value of the event entity
OPEN propertyRels[ where];
av: AttributeValue ← [ attribute: Event, lo: evEnt, hi: evEnt];
relSet: RelshipSet ← RelationSubset[ Rel, LIST[ av]];
tuple: Relship ← NextRelship[ relSet];
IF tuple = NIL THEN val ← NIL
ELSE val ← GetF[ tuple, PropValue];
ReleaseRelshipSet[ relSet];
RETURN[ val];
END; -- GetAValue
ListProperties: PUBLIC ENTRY PROCEDURE [ ev: Hickory.Event ← NIL] RETURNS [ pSet: PropertySet] = BEGIN
enumerates all property names in increasing order.
ENABLE UNWIND => NULL;
entSet: EntitySet;
ent: Entity;
success: BOOLEAN ← FALSE;
pSet.Head ← NIL; pSet.Tail ← NIL;
WHILE NOT success DO
ENABLE Aborted => { HickorySupport.RestartTransaction; success ← FALSE; LOOP };
HickorySupport.StartTransaction[];
IF ev = NIL THEN BEGIN
IF RopeSets.IsSetEmpty[ propertyCache] THEN BEGIN
entSet ← DomainSubset[ d: propertyDomain];
WHILE ( ent ← NextEntity[ entSet]) # NIL DO
name: Rope.ROPE ← NameOf[ ent];
propertyCache ← RopeSets.InsertValueIntoSet[ name, propertyCache];
ENDLOOP;
ReleaseEntitySet[ entSet];
END;
pSet ← RopeSets.Union[ propertyCache, [NIL, NIL]];
END
ELSE pSet ← GetPropertiesOfEvent[ ev];
MarkTransaction[ TransactionOf[ $Hickory]];
success ← TRUE;
ENDLOOP; -- Transaction
RETURN[ pSet];
END; -- ListProperties
GetPropertiesOfEvent: INTERNAL PROCEDURE [ ev: Hickory.Event] RETURNS [ pSet: PropertySet] = BEGIN
here we look into all the different propertyRels and find the names of properties
that the event has.
evEnt: Entity ← DeclareEntity[ eventDomain, ev, OldOnly];
IF evEnt = NIL THEN ERROR Error[ NoSuchEvent, ev];
FOR typ: HickoryStorage.PropertyTypes IN HickoryStorage.PropertyTypes DO
OPEN propertyRels[ typ];
av: AttributeValue ← [ attribute: Event, lo: evEnt, hi: evEnt];
relSet: RelshipSet ← RelationSubset[ Rel, LIST[ av]];
tuple: Relship;
WHILE ( tuple ← NextRelship[ relSet]) # NIL DO
pSet ← RopeSets.InsertValueIntoSet[ NameOf[ V2E[ GetF[ tuple, Property]]], pSet];
ENDLOOP;
ReleaseRelshipSet[ relSet];
ENDLOOP;
RETURN[ pSet];
END; -- GetPropertiesOfEvent
RemoveProperty: PUBLIC ENTRY PROCEDURE [ property: Property] = BEGIN
to destroy one property and remove it from all events that have the named property
OPEN propertyDescriptorRel;
ENABLE UNWIND => NULL;
propEnt: 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[];
propEnt ← DeclareEntity[ propertyDomain, property, OldOnly];
IF propEnt = NIL THEN ERROR Error[ NoSuchProperty, property];
DestroyEntity[ propEnt]; -- destroys all relships using the entity
MarkTransaction[ TransactionOf[ $Hickory]];
success ← TRUE;
ENDLOOP; -- TRansaction
propertyCache ← RopeSets.DeleteValueFromSet[ property, propertyCache];
HickoryNotify.NotifyClients[ PropertyDestroy, NIL, RopeSets.InsertValueIntoSet[ property, [NIL, NIL]]];
END; -- RemoveProperty
InvalidateCache: PUBLIC INTERNAL PROCEDURE = BEGIN
propertyCache.Head ← NIL; propertyCache.Tail ← NIL;
END; -- InvalidateCache
InitializeCache: PUBLIC INTERNAL PROCEDURE = BEGIN
to initialize the cache of HickoryProp
entSet: EntitySet;
ent: Entity;
propertyCache.Head ← NIL; propertyCache.Tail ← NIL;
entSet ← DomainSubset[ d: propertyDomain];
WHILE ( ent ← NextEntity[ entSet]) # NIL DO
prop: Property ← NameOf[ ent];
propertyCache ← RopeSets.InsertValueIntoSet[ prop, propertyCache];
ENDLOOP;
ReleaseEntitySet[ entSet];
END;
Initially
propertyCache.Head ← NIL; propertyCache.Tail ← NIL;
END.