-- File: DomainNutImpl.mesa
-- Contents: Implementation of the Domain Nut windows.
-- Last edited by:
-- Willie-Sue on: February 22, 1983 3:49 pm
-- Cattell on: June 6, 1983 1:23 pm
-- Maxwell on: June 3, 1982 9:27 am
-- Donahue on: April 11, 1983 4:56 pm

DIRECTORY
Buttons USING [ButtonProc],
DB,
InputFocus USING [SetInputFocus],
Menus USING [AppendMenuEntry, CreateMenu, Menu, MenuProc],
Nut,
NutOps,
NutViewer,
Rope,
Schema,
SystemNuts,
ViewerOps,
ViewerTools,
ViewerClasses;


DomainNutImpl: CEDAR PROGRAM
IMPORTS DB, Nut, NutOps, InputFocus, Menus, Schema,
   NutViewer, Rope, ViewerOps, ViewerTools
EXPORTS SystemNuts =

BEGIN OPEN DB, NutViewer, Schema, ViewerTools;

Viewer: TYPE = ViewerClasses.Viewer;

AttributeFieldObject: TYPE = RECORD[ attribute: Attribute, property: Attribute ];

AttributeFieldHandle: TYPE = REF AttributeFieldObject;

EntityFieldObject: TYPE = RECORD[ entity: Entity ];

EntityFieldHandle: TYPE = REF EntityFieldObject;

displayerMenu: Menus.Menu = Menus.CreateMenu[];
editorMenu: Menus.Menu = Menus.CreateMenu[];

BuildMenus: PROC = 
BEGIN OPEN NutViewer;
Menus.AppendMenuEntry[
displayerMenu, MakeMenuEntry[DBQueue[], "Edit", EditProc]];
Menus.AppendMenuEntry[
displayerMenu, MakeMenuEntry[DBQueue[], "Freeze", DefaultFreezeProc]];

Menus.AppendMenuEntry[
editorMenu, MakeMenuEntry[DBQueue[], "Erase", EraseProc]];
Menus.AppendMenuEntry[
editorMenu, MakeMenuEntry[DBQueue[], "Rename", RenameProc]];
Menus.AppendMenuEntry[
editorMenu, MakeMenuEntry[DBQueue[], "Save", SaveProc]];
Menus.AppendMenuEntry[
editorMenu, MakeMenuEntry[DBQueue[], "Reset", ResetProc]];
END;

EditProc: Menus.MenuProc =
-- Copied from NutDefaultImpl.EditDisplayerProc
{ viewer: Viewer = NARROW[parent];
  dInfo: DomainInfo = NARROW[ ViewerOps.FetchProp[viewer, $DomainInfo] ];
  seg: DB.Segment = dInfo.segment;
  dName: ROPE = dInfo.dName;
InputFocus.SetInputFocus[];
[] ← Nut.Edit[d: DomainDomain, eName: dName, parent: viewer, method: replace, seg: seg]};

DomainQueryer: PUBLIC Nut.QueryProc =
BEGIN
Message[NIL, "Domain queryers not implemented!"];
END;

----------------------------
DomainCreate: PUBLIC Nut.CreateProc =
{ RETURN[ Nut.DefaultCreate[nutType, d, eName, seg, column] ] };

DomainDisplayer: PUBLIC Nut.DisplayProc =
BEGIN
lastViewer: Viewer;
dInfo: DomainInfo = NEW[ DomainInfoRecord ];
ViewerOps.SetMenu[newV, displayerMenu];
dInfo.dName ← NameOf[e];
dInfo.segment ← seg;
dInfo.domain ← e;
lastViewer← NutViewer.Initialize[newV];
ViewerOps.AddProp[newV, $DomainInfo, dInfo];
IF NutOps.IsSystemDomain[e] THEN lastViewer ← NutViewer.MakeLabel["", lastViewer]
ELSE
{lastViewer← NutViewer.MakeLabel[
name: "Relation / attribute / uniqueness for attributes that reference this domain:",
sib: lastViewer];
lastViewer← BuildInfoWindowButtons[lastViewer, e]};
BuildEntityWindowButtons[lastViewer, e, seg];
ViewerOps.PaintViewer[newV, client];
END;

BuildInfoWindowButtons: PROC[lastViewer: Viewer, e: Entity] RETURNS[newLastViewer: Viewer]=
BEGIN rs: RelshipSet; r: Relship; other: Domain;
myAttrs: AttributeList = GetDomainRefAttributes[e];
FOR alT: AttributeList← myAttrs, alT.rest UNTIL alT=NIL DO
lastViewer← NutViewer.MakeButton[
  q: NutViewer.DBQueue[], name: GetName[V2E[GetP[alT.first, aRelationIs]]],
  proc: ProcessAttributeSelection,
  data: NEW[AttributeFieldObject← [alT.first, aRelationIs]],
  sib: lastViewer, newLine: TRUE];

lastViewer← NutViewer.MakeButton
  [q: NutViewer.DBQueue[], name: GetName[alT.first], proc: ProcessAttributeSelection,
  data: NEW[AttributeFieldObject← [alT.first, NIL]], sib: lastViewer];

lastViewer← NutViewer.MakeButton[
  q: NutViewer.DBQueue[], name: GetName[V2E[GetP[alT.first, aTypeIs]]],
  proc: ProcessAttributeSelection,
  data: NEW[AttributeFieldObject← [alT.first, aTypeIs]], sib: lastViewer];

lastViewer← NutViewer.MakeButton[
  q: NutViewer.DBQueue[], name: NutOps.GetUniquenessString[alT.first],
  proc: ProcessAttributeSelection,
  data: NEW[AttributeFieldObject← [alT.first, NIL]], sib: lastViewer];
ENDLOOP;
lastViewer← NutViewer.MakeLabel["Related domains:", lastViewer, TRUE];
rs← RelationSubset[dSubType, LIST[[dSubTypeIs, e]]];
UNTIL Null[r← NextRelship[rs]] DO
other← V2E[GetF[r, dSubTypeOf]];
lastViewer← NutViewer.MakeLabel["Supertype:", lastViewer, TRUE];
lastViewer← NutViewer.MakeButton[
  q: NutViewer.DBQueue[], name: GetName[other], proc: ProcessEntitySelection,
  data: NEW[EntityFieldObject← [other]], sib: lastViewer];
ENDLOOP;
ReleaseRelshipSet[rs];
rs← RelationSubset[dSubType, LIST[[dSubTypeOf, e]]];
UNTIL Null[r← NextRelship[rs]] DO
other← V2E[GetF[r, dSubTypeIs]];
lastViewer← NutViewer.MakeLabel["Subtype:", lastViewer, TRUE];
lastViewer← NutViewer.MakeButton[
  q: NutViewer.DBQueue[], name: GetName[other], proc: ProcessEntitySelection,
  data: NEW[EntityFieldObject← [other]], sib: lastViewer];
ENDLOOP;
ReleaseRelshipSet[rs];
RETURN[lastViewer]
END;

BuildEntityWindowButtons: PROC[lastViewer: Viewer, e: Entity, seg: Segment] =
-- display all the entities in domain e
BEGIN
count: INT← 0;
t: Entity;
myEntities: EntitySet;
lastViewer← NutViewer.MakeLabel[
 name: "List of the entities of this domain:",
 sib: lastViewer, newLine: TRUE];
IF Eq[e, DomainDomain] THEN {
lastViewer ← NewEntity[RelationDomain, lastViewer];
lastViewer ← NewEntity[AttributeDomain, lastViewer];
lastViewer ← NewEntity[DataTypeDomain, lastViewer];
lastViewer ← NewEntity[IndexDomain, lastViewer];
lastViewer ← NewEntity[IndexFactorDomain, lastViewer] };
IF Eq[e, RelationDomain] THEN
{ lastViewer ← NewEntity[dSubType, lastViewer];
lastViewer ← NewEntity[aRelation, lastViewer];
lastViewer ← NewEntity[aType, lastViewer];
lastViewer ← NewEntity[aUniqueness, lastViewer];
lastViewer ← NewEntity[aLength, lastViewer];
lastViewer ← NewEntity[aLink, lastViewer];
lastViewer ← NewEntity[ifIndex, lastViewer];
lastViewer ← NewEntity[ifAttribute, lastViewer] };
IF Eq[e, AttributeDomain] THEN {
[] ← NutViewer.MakeLabel[ name: "(Not implemented)", sib: lastViewer, newLine: TRUE];
RETURN};
IF Eq[e, IndexDomain] THEN {
[] ← NutViewer.MakeLabel[ name: "(Not implemented)", sib: lastViewer, newLine: TRUE];
RETURN};
IF Eq[e, IndexFactorDomain] THEN {
[] ← NutViewer.MakeLabel[ name: "(Not implemented)", sib: lastViewer, newLine: TRUE];
RETURN};
IF Eq[e, DataTypeDomain] THEN {
lastViewer ← NewEntity[StringType, lastViewer];
lastViewer ← NewEntity[IntType, lastViewer];
lastViewer ← NewEntity[BoolType, lastViewer];
lastViewer ← NewEntity[TimeType, lastViewer];
RETURN};
myEntities ←
IF Eq[e, DomainDomain] OR Eq[e, RelationDomain]
THEN DomainSubset[d: e, searchSegment: seg]
ELSE -- sort-- DomainSubset[e, "", "\177"];
UNTIL Null[t← NextEntity[myEntities]] DO
IF (count← count+1)>200
THEN {lastViewer← NutViewer.MakeLabel[
"... more than 200 entities: remainder truncated ...", lastViewer, TRUE]; EXIT}
ELSE lastViewer← NewEntity[t, lastViewer];
ENDLOOP;
ReleaseEntitySet[myEntities];
END;

NewEntity: PROC[t: Entity, sib: Viewer] RETURNS[Viewer] =
{RETURN[NutViewer.MakeButton[
  q: NutViewer.DBQueue[], name: GetName[t], proc: ProcessEntitySelection,
  data: NEW[EntityFieldObject← [t]], sib: sib, newLine: TRUE]]};

ProcessEntitySelection: Buttons.ButtonProc =
BEGIN
 fd: EntityFieldHandle = NARROW[clientData];
 viewer: Viewer = NARROW[parent];
 dInfo: DomainInfo = NARROW[ ViewerOps.FetchProp[viewer.parent, $DomainInfo] ];
 []← Nut.Display[e: fd.entity, parent: viewer.parent, seg: dInfo.segment]
END;

ProcessAttributeSelection: Buttons.ButtonProc =
BEGIN
 fd: AttributeFieldHandle = NARROW[clientData];
 v: Viewer = NARROW[parent];
 dInfo: DomainInfo = NARROW[ ViewerOps.FetchProp[v.parent, $DomainInfo] ];
IF fd.property = NIL THEN Message[v, "Not an entity-valued field"]
ELSE
[]← Nut.Display[ e: V2E[GetP[fd.attribute, fd.property]], parent: v.parent, seg: dInfo.segment];
END;

RenameProc: Menus.MenuProc =
BEGIN
  viewer: Viewer = NARROW[parent];
dInfo: DomainInfo = NARROW[ViewerOps.FetchProp[viewer, $DomainInfo]];
dInfo.dName← ViewerTools.GetSelectionContents[];
IF dInfo.domain#NIL THEN SetName[dInfo.domain, dInfo.dName];
viewer.name← Rope.Cat["Domain: ", dInfo.dName];
ViewerOps.PaintViewer[viewer, caption];
END;

ResetProc: Menus.MenuProc =
BEGIN
viewer: Viewer = NARROW[parent];
dInfo: DomainInfo = NARROW[ViewerOps.FetchProp[viewer, $DomainInfo]];
viewer.child ← NIL;
ViewerOps.PaintViewer[viewer, client];
DomainEditor[DomainDomain, GetContents[dInfo.name], viewer, dInfo.segment];
END;

SaveProc: Menus.MenuProc =
BEGIN
  viewer: Viewer = NARROW[parent];
dInfo: DomainInfo = NARROW[ViewerOps.FetchProp[viewer, $DomainInfo]];
SaveDomain[viewer];
END;

EraseProc: Menus.MenuProc =
BEGIN
viewer: Viewer = NARROW[parent];
dInfo: DomainInfo = NARROW[ViewerOps.FetchProp[viewer, $DomainInfo]];
name: ROPE = IF dInfo # NIL THEN GetContents[dInfo.name] ELSE NIL;
domain: Domain = IF name # NIL THEN
      DeclareDomain[name, dInfo.segment, OldOnly] ELSE NIL;
subTypes: BOOLEANFALSE;
IF domain = NIL THEN RETURN;
DestroyDomain[domain ! DB.Error =>
{ IF code = NotImplemented THEN {subTypes ← TRUE; CONTINUE} }];
IF subTypes THEN
 {Message[viewer, "You must delete the subtypes first."]; RETURN}
END;

-- Domain Editor --



DomainEditor: PUBLIC Nut.EditProc = -- d: Domain, eName: ROPE, newV: Viewer
BEGIN
rope: ROPE;
rs: RelshipSet;
lastV: Viewer;
relation: Relation;
info: DomainInfo = NEW[DomainInfoRecord];
subTypeRS: Relship;
isAttribute: Attribute;
list: LIST OF Attribute;
attributes: AttributeList;
segment: Segment = seg;
IF Rope.Equal[eName, "Domain", FALSE] THEN {
[] ← Nut.Edit[d, "NEW", segment, newV]; RETURN};
IF Rope.Equal[eName, "Relation", FALSE] THEN {
[] ← Nut.Edit[RelationDomain, "NEW", segment, newV]; RETURN};
IF Rope.Equal[eName, "Attribute", FALSE] OR Rope.Equal[eName, "DataType", FALSE]
THEN {[] ← NutViewer.Message[NIL,
eName, " is a system entity. You may not edit it."]; RETURN};
ViewerOps.SetMenu[newV, editorMenu];
lastV← NutViewer.Initialize[newV];
info.segment ← segment;
info.domain ← FetchEntity[d, eName, segment];
lastV ← NutViewer.MakeLabel["", lastV];
info.name← lastV← NutViewer.NextRightTextViewer[lastV, 400];
lastV ← NutViewer.MakeLabel["superTypes:", lastV, FALSE];
info.superTypes← lastV ← NutViewer.NextRightTextViewer[lastV, 400];
lastV ← NutViewer.MakeLabel["subTypes:", lastV, TRUE];
info.subTypes ← NutViewer.NextRightTextViewer[lastV, 400];
lastV ← NutViewer.MakeButton[
q: NutViewer.DBQueue[], name: "NEW PROPERTY", proc: NewAttribute, sib: lastV,
border: TRUE, newLine: TRUE];
SetContents[info.name, eName];
IF info.domain = NIL THEN {ViewerOps.AddProp[newV, $DomainInfo, info]; RETURN};
-- print the subtypes of the domain
rope ← NIL;
rs ← RelationSubset[dSubType, LIST[[dSubTypeOf, info.domain]]];
WHILE (subTypeRS ← NextRelship[rs]) # NIL DO
IF rope # NIL THEN rope ← Rope.Concat[rope, ", "];
rope ← Rope.Concat[rope, GetFS[subTypeRS, dSubTypeIs]];
ENDLOOP;
ReleaseRelshipSet[rs];
SetContents[info.subTypes, rope];
info.subTypes.newVersion ← FALSE;
-- print the supertypes of the domain
rope ← NIL;
rs ← RelationSubset[dSubType, LIST[[dSubTypeIs, info.domain]]];
WHILE (subTypeRS ← NextRelship[rs]) # NIL DO
IF rope # NIL THEN rope ← Rope.Concat[rope, ", "];
rope ← Rope.Concat[rope, GetFS[subTypeRS, dSubTypeOf]];
ENDLOOP;
ReleaseRelshipSet[rs];
SetContents[info.superTypes, rope];
info.superTypes.newVersion ← FALSE;
-- print the current properties
list ← NutOps.GetRefAttributes[info.domain];
FOR list ← list, list.rest WHILE list # NIL DO -- find all of the "of" attributes
IF Null[list.first] THEN LOOP;
IF ~Rope.Equal[GetName[list.first], "of"] THEN LOOP;
IF ~Eq[V2E[GetP[list.first, aTypeIs]], info.domain] THEN LOOP; -- skip superclasses
IF LOOPHOLE[V2U[GetP[list.first, aUniquenessIs]], Uniqueness] # Key THEN LOOP;
-- we have a likely candidate
relation ← NutOps.GetRelation[list.first];
attributes ← NutOps.AttributesOf[relation];
IF attributes = NIL OR attributes.rest = NIL OR attributes.rest.rest # NIL THEN LOOP;
isAttribute ← NIL;
IF Rope.Equal[GetName[attributes.first], "is"] THEN isAttribute ← attributes.first;
IF Rope.Equal[GetName[attributes.rest.first], "is"] THEN isAttribute ← attributes.rest.first;
IF isAttribute = NIL THEN LOOP;
info.properties ← CONS[DisplayAttribute[relation, isAttribute, lastV], info.properties];
lastV ← info.properties.first.length;
ENDLOOP;
info.properties ← Reverse[info.properties];
ViewerOps.AddProp[newV, $DomainInfo, info];
END;

SaveDomain: PROCEDURE[viewer: Viewer] =
BEGIN
new: Domain;
info: DomainInfo;
ok, copy: BOOLEANTRUE;
info ← NARROW[ViewerOps.FetchProp[viewer, $DomainInfo]];
-- will we have to copy the domain?
IF info.domain = NIL THEN copy ← TRUE
ELSE IF ~Optimized[info.domain] THEN copy ← FALSE
ELSE {subType: ROPE;
[subType, , ] ← NextName[GetContents[info.subTypes]];
copy ← (subType.Length[] # 0)};
IF copy THEN new ← DeclareDomain[NIL, info.segment] ELSE new ← info.domain;
-- check to see if this is a legal edit
IF ~CheckTypeList[GetContents[info.superTypes], info.segment] THEN ok ← FALSE;
IF ~CheckTypeList[GetContents[info.subTypes], info.segment] THEN ok ← FALSE;
FOR list: LIST OF AttributeInfo ← info.properties, list.rest WHILE list # NIL DO
IF ~CheckProperty[new, list.first, info.deleted] THEN ok ← FALSE;
ENDLOOP;
IF ~ok THEN {IF copy THEN DestroyDomain[new]; RETURN};
-- WE ARE COMMITTED BEYOND THIS POINT
-- handle the SubTypeRelations
IF info.domain # NIL THEN
{RemoveSubTypes[info.domain]; RemoveSuperTypes[info.domain]};
AddSubTypes[new, GetContents[info.subTypes], info.segment];
AddSuperTypes[new, GetContents[info.superTypes], info.segment];
-- insert the new properties
FOR list: LIST OF AttributeInfo ← info.properties, list.rest WHILE list # NIL DO
info.deleted ← RemoveDeleted[list.first, info.deleted];
SaveProperty[new, list.first];
ENDLOOP;
-- destroy the properties deleted by the user
FOR list: LIST OF Relation ← info.deleted, list.rest WHILE list # NIL DO
DestroyRelation[list.first];
ENDLOOP;
-- copy the domain's contents
IF info.domain # NIL AND copy THEN {
Schema.CopyDomainContents[info.domain, new];
RemoveSubTypes[info.domain];
DestroyDomain[info.domain]};
SetName[new, GetContents[info.name]];
viewer.child ← NIL;
ViewerOps.AddProp[viewer, $DomainInfo, NIL];
[] ← Nut.Display[e: new, parent: viewer, method: replace, seg: info.segment];
END;

CheckTypeList: PROCEDURE[types: ROPE, seg: Segment] RETURNS[ok: BOOLEANTRUE] =
BEGIN
d: Domain;
type: ROPE;
WHILE types.Length[] > 0 DO
[type, types, ok] ← NextName[types];
IF ~ok THEN EXIT;
IF type.Length[] = 0 THEN LOOP;
d ← FetchEntity[DomainDomain, type, seg];
IF d # NIL THEN LOOP;
Message[NIL, Rope.Cat["BAD TYPE: ", type]];
RETURN[FALSE];
ENDLOOP;
IF ~ok THEN Message[NIL, "BAD TYPE LIST."];
END;

RemoveSubTypes: PROCEDURE[d: Domain] =
BEGIN
rs: RelshipSet;
subTypeRS: Relship;
rs ← RelationSubset[dSubType, LIST[[dSubTypeOf, d]]];
WHILE (subTypeRS ← NextRelship[rs]) # NIL DO
DestroySubType[of: d, is: V2E[GetF[subTypeRS, dSubTypeIs]]];
ENDLOOP;
ReleaseRelshipSet[rs];
END;

RemoveSuperTypes: PROCEDURE[d: Domain] =
BEGIN
rs: RelshipSet;
subTypeRS: Relship;
rs ← RelationSubset[dSubType, LIST[[dSubTypeIs, d]]];
WHILE (subTypeRS ← NextRelship[rs]) # NIL DO
DestroySubType[is: d, of: V2E[GetF[subTypeRS, dSubTypeOf]]];
ENDLOOP;
ReleaseRelshipSet[rs];
END;

AddSuperTypes: PROCEDURE[d: Domain, types: ROPE, seg: Segment] =
BEGIN
type: ROPE;
new: Domain;
superType: Domain;
WHILE types.Length[] > 0 DO
[type, types, ] ← NextName[types];
IF type.Length[] = 0 THEN LOOP;
superType ← FetchEntity[DomainDomain, type, seg];
DeclareSubType[of: superType, is: d
! DB.Error => IF code=NotImplemented THEN RESUME]; -- we know better than DB!
IF ~Optimized[superType] THEN LOOP;
new ← DeclareDomain[NIL, SegmentOf[d]];
Schema.CopyDomainContents[superType, new];
RemoveSubTypes[superType];
DestroyDomain[superType];
SetName[new, type];
ENDLOOP;
END;

AddSubTypes: PROCEDURE[d: Domain, types: ROPE, seg: Segment] =
BEGIN
type: ROPE;
subType: Domain;
WHILE types.Length[] > 0 DO
[type, types, ] ← NextName[types];
IF type.Length[] = 0 THEN LOOP;
subType ← FetchEntity[DomainDomain, type, seg];
DeclareSubType[of: d, is: subType
! DB.Error => IF code=NotImplemented THEN RESUME]; -- we know better than DB!
ENDLOOP;
END;

DomainQuery: PUBLIC Nut.QueryProc = { Nut.DefaultQuery[d, newV, segment] };

-- start code
BuildMenus[];

END.

Change log.

Willie-Sue December 13, 1982: aFooProp => aFooIs, for new system properties