File: DomainNutImpl.mesa
Contents: Implementation of the Domain Nut windows.
Last edited by:
Willie-Sue on: February 22, 1983 3:49 pm
Cattell on: October 5, 1983 10:26 am
Maxwell on: June 3, 1982 9:27 am
Donahue on: July 17, 1984 4:58:17 pm PDT
Butler on: June 27, 1984 3:57:51 pm PDT
DIRECTORY
Buttons USING [ButtonProc],
Containers USING [ChildYBound],
DB,
DefaultNutUtilities,
Menus USING [AppendMenuEntry, CreateMenu, Menu, MenuProc],
Nut,
NutOps,
NutViewer,
Rope,
SchemaNut,
ViewerOps, ViewerTools, ViewerClasses;
Need to call ReleaseEntitySet[myEntities] when viewer destroyed!
Can't use MBQueue's clientData
DomainNutImpl:
CEDAR
PROGRAM
IMPORTS Containers,
DB, DefaultNutUtilities, Nut, NutOps, Menus,
SchemaNut, NutViewer, Rope, ViewerOps, ViewerTools =
BEGIN OPEN DB, NutViewer, ViewerTools;
Viewer: TYPE = ViewerClasses.Viewer;
AttributeFieldObject: TYPE = RECORD[ attribute: Attribute, property: Attribute ];
AttributeFieldHandle: TYPE = REF AttributeFieldObject;
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[
displayerMenu, MakeMenuEntry[DBQueue[], "ShowDomainInfo", ShowDomainInfoProc]];
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 =
BEGIN
viewer: Viewer = NARROW[parent];
entity: ROPE;
segment: DB.Segment;
[segment: segment, entity: entity] ← Nut.GetNutInfo[viewer];
[] ← DomainEditor[eName: entity, domain: "Domain", segment: segment, lastSpawned: viewer];
END;
ShowDomainInfoProc: Menus.MenuProc = {
Replace client portion of window, to show entities instead of schema info for domain,
or vice versa.
vp: Viewer = NARROW[parent];
mode: ATOM ← NARROW[ ViewerOps.FetchProp[vp, $DisplayMode]];
segment: DB.Segment;
entity, domain: Rope.ROPE;
[segment, domain, entity] ← Nut.GetNutInfo[vp];
vp.child← NIL; -- Throw away children
IF mode=$Entities
AND
NOT NutOps.IsSystemDomain[domain]
THEN {
vp.scrollable← TRUE;
ViewerOps.AddProp[vp, $DisplayMode, $Info];
[]← BuildInfoWindowButtons[NutViewer.Initialize[vp], DB.DeclareDomain[entity, segment]]
}
ELSE {
vp.scrollable← FALSE;
ViewerOps.AddProp[vp, $DisplayMode, $Entities];
ShowEntitiesInWindow[vp, DB.DeclareDomain[entity, segment], segment];
};
ViewerOps.EstablishViewerPosition[vp, vp.wx, vp.wy, vp.ww, vp.wh];
ViewerOps.PaintViewer[viewer: vp, hint: client, clearClient: TRUE];
};
DomainQueryer:
PUBLIC Nut.NutProc =
BEGIN
[] ← NutViewer.Message[NIL, "Domain queryers not implemented!"];
END;
DomainDisplayer:
PUBLIC Nut.NutProc =
BEGIN
e: DB.Entity ← DB.DeclareDomain[eName, segment, OldOnly];
v ← NutViewer.ReplaceViewer[eName, domain, segment, lastSpawned];
ViewerOps.SetMenu[v, displayerMenu];
ViewerOps.AddProp[v, $DisplayMode, $Entities];
ViewerOps.AddProp[v, $Domain, e];
IF ViewerOps.FetchProp[v, $DomainInfo] =
NIL
THEN ViewerOps.AddProp[v, $DomainInfo, NEW[SchemaNut.DomainInfoRecord]];
ShowEntitiesInWindow[v, e, segment];
ViewerOps.PaintViewer[v, all];
END;
GetDomain:
PROC[v: Viewer]
RETURNS[d:
DB.Domain] = {
this procedure returns either the cached $Domain property or it computes the domain from the NutInfo values
d ← DB.V2E[ViewerOps.FetchProp[v, $Domain]];
IF NOT DB.Null[d] THEN RETURN;
{ segment:
DB.Segment;
entity: Rope.ROPE;
domain: DB.Domain;
[entity: entity, segment: segment] ← Nut.GetNutInfo[v];
domain ← DB.DeclareDomain[entity, segment, OldOnly];
cache the result
ViewerOps.AddProp[v, $Domain, domain];
RETURN[ domain ] } };
BuildInfoWindowButtons:
PROC[lastViewer: Viewer, e: Entity]
RETURNS[newLastViewer: Viewer] =
Display information about domain if showSchemaNut.DomainInfo, else show entities.
BEGIN rs: RelshipSet; r: Relship; other: Domain;
myAttrs: AttributeList = GetDomainRefAttributes[e];
lastViewer← NutViewer.MakeLabel[
name: "* Relation / attribute / uniqueness for attributes that reference this domain *",
sib: lastViewer];
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: 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: other, sib: lastViewer];
ENDLOOP;
ReleaseRelshipSet[rs];
RETURN[lastViewer]
END;
ShowEntitiesInWindow:
PROC[parent: Viewer, e: Entity, seg: Segment] =
Display all the entities in domain e
BEGIN
count: INT← 0;
lastViewer: Viewer;
myEntities: EntitySet;
IF Eq[e, DomainDomain]
THEN {
lastViewer ← NutViewer.Initialize[parent];
lastViewer ← NewEntity[DataTypeDomain, lastViewer];
lastViewer ← NewEntity[RelationDomain, lastViewer];
myEntities ← DomainSubset[d: e, searchSegment: seg];
UNTIL Null[e← NextEntity[myEntities]]
DO
lastViewer← NewEntity[e, lastViewer] ENDLOOP;
ReleaseEntitySet[myEntities];
RETURN};
IF Eq[e, RelationDomain]
THEN {
lastViewer ← NutViewer.Initialize[parent];
myEntities ← DomainSubset[d: e, searchSegment: seg];
UNTIL Null[e← NextEntity[myEntities]]
DO
lastViewer← NewEntity[e, lastViewer] ENDLOOP;
ReleaseEntitySet[myEntities];
RETURN};
IF Eq[e, DataTypeDomain]
THEN {
lastViewer ← NutViewer.Initialize[parent];
lastViewer ← NewEntity[RopeType, lastViewer];
lastViewer ← NewEntity[IntType, lastViewer];
lastViewer ← NewEntity[BoolType, lastViewer];
lastViewer ← NewEntity[TimeType, lastViewer];
RETURN};
IF Eq[e, AttributeDomain]
OR Eq[e, IndexDomain]
OR Eq[e, IndexFactorDomain]
THEN
RETURN;
parent.scrollable← FALSE; -- Scrollbar will be provided by MBWindow
lastViewer← NutViewer.CreateMBWindow[
count: GetThingCount,
thumb: DomainThumb,
next: DomainNextProc,
prev: DomainPrevProc,
buttonProc: ProcessEntitySelection,
info: [parent: parent, wx: 0, wy: 0, ww: parent.ww, wh: 800, border: FALSE],
q: NIL ];
Containers.ChildYBound[container: parent, child: lastViewer];
END;
GetThingCount: NutViewer.CountProc = {
parent: Viewer = GetTopLevel[v];
eName: Rope.ROPE = Nut.GetNutInfo[parent].entity;
SELECT
TRUE
FROM
eName.Equal["Person"] => RETURN[1000];
eName.Equal["Organization"] => RETURN[500];
ENDCASE => RETURN[EntitySetSize[DomainSubset[GetDomain[parent], "", "\177"], FALSE]];
};
GetTopLevel:
PROC [v: Viewer]
RETURNS [parent: Viewer] ~ {
parent ← v; WHILE parent.parent # NIL DO parent ← parent.parent ENDLOOP
};
EntitySetSize:
PROC [awesome: EntitySet, forSure:
BOOL]
RETURNS [totally:
INT] = {
Estimate total size of an EntitySet. If it's larger than 100, just guess 500.
totally← 0;
UNTIL
DB.NextEntity[awesome]=
NIL
DO
IF (totally← totally+1)>100 AND NOT forSure THEN {totally← 500; EXIT}; -- just estimate
ENDLOOP;
DB.ReleaseEntitySet[awesome];
RETURN[totally]
};
DomainNextProc: NutViewer.EnumProc =
TRUSTED
BEGIN es: EntitySet ← LOOPHOLE[enum];
e: Entity ← NextEntity[es];
RETURN[IF e=NIL THEN NIL ELSE NutOps.SafeNameOf[e], e];
END;
DomainPrevProc: NutViewer.EnumProc =
TRUSTED
BEGIN es: EntitySet← LOOPHOLE[enum];
e: Entity← PrevEntity[es];
RETURN[IF e=NIL THEN NIL ELSE NutOps.SafeNameOf[e], e];
END;
DomainThumb: NutViewer.ThumbProc =
TRUSTED
BEGIN es: EntitySet ← LOOPHOLE[enum];
domain: DB.Domain = GetDomain[ GetTopLevel[v] ];
DB.ReleaseEntitySet[es];
IF where<10
THEN
es← DB.DomainSubset[domain, "", "\177", First]
ELSE
IF where>90
THEN {
es← DB.DomainSubset[domain, "", "\177", Last];
[]← PrevEntity[es]; []← PrevEntity[es]; []← PrevEntity[es]}
ELSE {
Get there the hard way, though NextEntity calls
es← DB.DomainSubset[domain, "", "\177", First];
THROUGH [0..where*GetThingCount[v, es]/100) DO []← NextEntity[es] ENDLOOP;
};
RETURN[es]
END;
NewEntity:
PROC[e: Entity, sib: Viewer]
RETURNS[Viewer] =
{
RETURN[NutViewer.MakeButton[
q: NutViewer.DBQueue[], name: DB.NameOf[e], proc: ProcessEntitySelection,
data: e, sib: sib, newLine: TRUE]]};
ProcessEntitySelection: Buttons.ButtonProc =
Used with NutViewer buttons
BEGIN
e: Entity = V2E[clientData];
viewer: Viewer = GetTopLevel[NARROW[parent]];
segment: DB.Segment = Nut.GetNutInfo[viewer].segment;
eName: ROPE ← NutOps.SafeNameOf[e];
IF e=NIL THEN RETURN; -- really not a button
[] ← Nut.Display[eName: eName, domain: DB.NameOf[DB.DomainOf[e]], segment: segment, parent: viewer];
END;
ProcessAttributeSelection: Buttons.ButtonProc =
BEGIN
fd: AttributeFieldHandle = NARROW[clientData];
v: Viewer = NARROW[parent];
IF fd.property =
NIL
THEN
Message[v, "Not an entity-valued field"]
ELSE
BEGIN
e: DB.Entity ← V2E[GetP[fd.attribute, fd.property]];
eName: ROPE ← NutOps.SafeNameOf[e];
IF
NOT
DB.Null[e]
THEN [] ← Nut.Display[eName: eName, domain: DB.NameOf[DB.DomainOf[e]], segment: Nut.GetNutInfo[v].segment, parent: v];
END;
END;
RenameProc: Menus.MenuProc =
BEGIN
viewer: Viewer = NARROW[parent];
domain: DB.Domain = GetDomain[viewer];
newName: Rope.ROPE = ViewerTools.GetSelectionContents[];
IF domain#NIL THEN DB.ChangeName[domain, newName];
viewer.name← Rope.Cat["Domain: ", newName];
ViewerOps.PaintViewer[viewer, caption];
Nut.ChangeName[viewer, newName]
END;
ResetProc: Menus.MenuProc =
BEGIN
viewer: Viewer = NARROW[parent];
eName: Rope.ROPE;
segment: DB.Segment;
[entity: eName, segment: segment] ← Nut.GetNutInfo[viewer];
DefaultNutUtilities.Reset[eName: eName, domain: "DomainDomain", seg: segment, viewer: viewer];
END;
SaveProc: Menus.MenuProc =
BEGIN
SaveDomain[NARROW[parent]];
END;
EraseProc: Menus.MenuProc =
BEGIN
viewer: Viewer = NARROW[parent];
domain: Domain = GetDomain[viewer];
IF domain = NIL THEN RETURN;
DestroyDomain[domain !
DB.Error =>
{
IF code = NotImplemented
THEN Message[viewer, "You must delete the subtypes first."];
CONTINUE }]
END;
Domain Editor --
DomainEditor:
PUBLIC Nut.NutProc =
-- d: Domain, eName: ROPE, lastSpawned: Viewer
BEGIN
rope: ROPE;
rs: RelshipSet;
lastV: Viewer;
relation: Relation;
info: SchemaNut.DomainInfo = NEW[SchemaNut.DomainInfoRecord];
subTypeRS: Relship;
isAttribute: Attribute;
list: LIST OF Attribute;
attributes: AttributeList;
d: DB.Domain;
IF Rope.Equal[eName, "Attribute",
FALSE]
OR Rope.Equal[eName, "DataType",
FALSE]
OR Rope.Equal[eName, "Domain", FALSE] OR Rope.Equal[eName, "Relation", FALSE]
THEN BEGIN
[] ← NutViewer.Message[NIL, eName, " is a system entity. You may not edit it."];
RETURN
END;
v ← NutViewer.ReplaceViewer[eName, domain, segment, lastSpawned];
ViewerOps.SetMenu[v, editorMenu];
ViewerOps.AddProp[v, $DomainInfo, info];
d ← GetDomain[v];
lastV← NutViewer.Initialize[v];
lastV ← NutViewer.MakeLabel["", lastV];
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: SchemaNut.NewAttribute, sib: lastV,
border:
TRUE, newLine:
TRUE];
IF DB.Null[GetDomain[v]] THEN RETURN;
print the subtypes of the domain
rope ← NIL;
rs ← RelationSubset[dSubType, LIST[[dSubTypeOf, d]]];
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, d]]];
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[d];
FOR list ← list, list.rest
WHILE list #
NIL
DO
-- find all of the "of" attributes
un: Uniqueness;
IF Null[list.first] THEN LOOP;
IF ~Rope.Equal[GetName[list.first], "of"] THEN LOOP;
IF ~Eq[V2E[GetP[list.first, aTypeIs]], d] THEN LOOP; -- skip superclasses
IF (un← 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[SchemaNut.DisplayAttribute[relation, isAttribute, lastV], info.properties];
lastV ← info.properties.first.length;
ENDLOOP;
info.properties ← SchemaNut.Reverse[info.properties];
ViewerOps.PaintViewer[v, all]
END;
SaveDomain:
PROCEDURE[viewer: Viewer] =
BEGIN
new: Domain;
info: SchemaNut.DomainInfo = NARROW[ViewerOps.FetchProp[viewer, $DomainInfo]];
oldDomain: DB.Domain = GetDomain[viewer];
eName: Rope.ROPE;
segment: DB.Segment;
ok, copy: BOOLEAN ← TRUE;
Will we have to copy the domain?
[entity: eName, segment: segment] ← Nut.GetNutInfo[viewer];
IF DB.Null[oldDomain] THEN copy ← TRUE
ELSE IF ~SchemaNut.Optimized[oldDomain] THEN copy ← FALSE
ELSE
BEGIN
subType: ROPE = SchemaNut.NextName[GetContents[info.subTypes]].token;
copy ← (subType.Length[] # 0);
END;
IF copy THEN new ← DeclareDomain[NIL, segment] ELSE new ← oldDomain;
Check to see if this is a legal edit
IF ~CheckTypeList[GetContents[info.superTypes], segment] THEN ok ← FALSE;
IF ~CheckTypeList[GetContents[info.subTypes], segment] THEN ok ← FALSE;
FOR list:
LIST
OF SchemaNut.AttributeInfo ← info.properties, list.rest
WHILE list #
NIL
DO
IF ~SchemaNut.CheckProperty[new, list.first, info.deleted] THEN ok ← FALSE;
ENDLOOP;
IF ~ok AND copy THEN { DestroyDomain[new]; RETURN };
WE ARE COMMITTED BEYOND THIS POINT
handle the SubTypeRelations
IF
NOT
DB.Null[oldDomain]
THEN
BEGIN
RemoveSubTypes[oldDomain];
RemoveSuperTypes[oldDomain];
END;
AddSubTypes[new, GetContents[info.subTypes], segment];
AddSuperTypes[new, GetContents[info.superTypes], segment];
Insert the new properties
FOR list:
LIST
OF SchemaNut.AttributeInfo ← info.properties, list.rest
WHILE list #
NIL
DO
info.deleted ← SchemaNut.RemoveDeleted[oldDomain, list.first, info.deleted];
SchemaNut.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
NOT
DB.Null[oldDomain]
AND copy
THEN
BEGIN
SchemaNut.CopyDomainContents[oldDomain, new];
RemoveSubTypes[oldDomain];
DestroyDomain[oldDomain];
END;
SetName[new, eName];
ViewerOps.AddProp[viewer, $DomainInfo, NIL];
[] ← DomainDisplayer[eName: eName, domain: "DomainDomain", segment: segment, lastSpawned: viewer];
END;
CheckTypeList:
PROCEDURE[types:
ROPE, seg: Segment]
RETURNS[ok:
BOOLEAN ←
TRUE] =
BEGIN
d: Domain;
type: ROPE;
WHILE types.Length[] > 0
DO
[type, types, ok] ← SchemaNut.NextName[types];
IF ~ok THEN EXIT;
IF type.Length[] = 0 THEN LOOP;
d ← FetchEntity[DomainDomain, type, seg];
IF d # NIL THEN LOOP;
NutViewer.Message[NIL, Rope.Cat["Bad type: ", type]];
RETURN[FALSE];
ENDLOOP;
IF ~ok THEN NutViewer.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, ] ← SchemaNut.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 ~SchemaNut.Optimized[superType] THEN LOOP;
new ← DeclareDomain[NIL, NutOps.SafeSegmentOf[d]];
SchemaNut.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, ] ← SchemaNut.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;
Start code
BuildMenus[];
Nut.Register[ "Domain", NIL, DomainDisplayer, DomainEditor];
END.