-- File: NutDumpImpl.mesa
-- Contents: Program to do ascii database output.
-- Status: Runs in Cedar as subordinate of Squirrel.
-- Created by: Rick Cattell, March 1980; stolen from DBDump created September 1980
-- Last Edited by:
-- Cattell on: June 3, 1983 2:26 pm
-- Maxwell on: June 4, 1982 12:10 pm
-- Willie-Sue on: February 22, 1983 3:47 pm
-- Donahue on: April 5, 1983 10:13 am

-- Output formats:
-- SegmentName [other info] on the first line

-- /Domain\name\nametype\ ...for each domain
-- /Relation\name\ ...for each relation
-- /Attribute\name\relation\type\cardinality\length\link\ ...for each attribute
-- /Index\relation\attribute\...\\ .. for each index
-- /domain\name-elt1\name-elt2\...\ ...for each entity
-- /relation\attr1:val1\attr2:val2\...\ ...for each relship

-- In entity-centric mode, prints entities and relships that ref them in first attribute together.

DIRECTORY
Atom USING [MakeAtom],
DBEnvironment USING [InternalError],
DB,
FileIO USING[Open],
IO,
NutDump,
NutOps,
NutViewer,
Rope,
SquirrelTool,
System,
ViewerClasses;

NutDumpImpl: CEDAR PROGRAM
IMPORTS
Atom, DBEnvironment, DB, FileIO, IO, NutOps, NutViewer, Rope, SquirrelTool, System
EXPORTS
NutDump -- DumpToFile -- =

BEGIN OPEN DB, IO, NutViewer;

inf: CARDINAL = 177777B;

IllegalEntity: SIGNAL = CODE;
IllegalFormat: SIGNAL = CODE;

squirrel: ViewerClasses.Viewer = SquirrelTool.squirrel;
elapsedTime: System.GreenwichMeanTime;
entityCentricFlag: BOOL;


word: PROC [u: UNSPECIFIED] RETURNS [IO.Value] =
-- temporary kluge for output of UNSPECIFIED to IO
INLINE {RETURN[[integer[LOOPHOLE[u, INTEGER]]]]};


-- Main procedure, exported to NutDump

DumpToFile: PUBLIC PROC[segment: ROPE, fileName: ROPE, dl: LIST OF DB.Domain ← NIL,
rl: LIST OF DB.Relation ← NIL, complement: BOOLFALSE, entityCentric: BOOLFALSE] =
TRUSTED BEGIN
{ENABLE ABORTED => GO TO SqueakyClean;
file: IO.Handle;
seg: Segment;
found: BOOLEANFALSE;
elapsedTime← System.GetGreenwichMeanTime[];
entityCentricFlag← entityCentric;
IF segment=NIL OR segment.Length[] = 0 THEN
{ Message[squirrel, "Must specify a segment to dump"]; RETURN};

IF fileName = NIL THEN fileName ← segment.Concat[".dump"];
file ← OpenFile[fileName];
seg← Atom.MakeAtom[segment];
FOR sl: LIST OF Segment ← DB.GetSegments[], sl.rest UNTIL sl = NIL DO
IF sl.first = seg THEN { found ← TRUE; EXIT } ENDLOOP;
IF NOT found THEN { Message[squirrel, segment, " not open; aborting dump"]; RETURN };
Message[squirrel, "Dumping to ", fileName, ": "];
MessageRope[squirrel, "Data schema ..."];
IF complement THEN {
dl ← Complement[dl, EntitySetToList[DomainSubset[d: DomainDomain, searchSegment: seg]]];
rl ← Complement[rl, EntitySetToList[DomainSubset[d: RelationDomain, searchSegment: seg]]]};

file.Put[rope[segment], rope[" ["], time[]];
file.Put[rope["]\n"]];
WriteSchema[file, dl, rl];
MessageRope[squirrel, "Domains: "];
-- Output Domain contents
FOR list: LIST OF Domain ← dl, list.rest WHILE list # NIL AND NOT SquirrelTool.stopped DO
MessageRope[squirrel, GetName[list.first]]; MessageRope[squirrel, "... "];
WriteDomain[file, list.first];
ENDLOOP;
IF entityCentric THEN {
MessageRope[squirrel, "Domain references: "];
FOR list: LIST OF Domain ← dl, list.rest WHILE list # NIL AND NOT SquirrelTool.stopped DO
MessageRope[squirrel, GetName[list.first]]; MessageRope[squirrel, "... "];
WriteDomain[file, list.first, TRUE];
ENDLOOP;
MessageRope[squirrel, "Other "];
-- Output Relation contents
};
MessageRope[squirrel, "Relations: "];
-- Output Relation contents
FOR list: LIST OF Relation ← rl, list.rest WHILE list # NIL AND NOT SquirrelTool.stopped DO
IF entityCentric AND FirstAttributeIsDomain[list.first] THEN LOOP;
MessageRope[squirrel, GetName[list.first]]; MessageRope[squirrel, "... "];
WriteRelation[file, list.first];
ENDLOOP;
CloseFile[file];
elapsedTime← LOOPHOLE[System.GetGreenwichMeanTime[] - elapsedTime];
Message[squirrel, PutFR["Dump completed: %g elapsed seconds.\n", int[elapsedTime]] ];
EXITS SqueakyClean => {}};
END;

FirstAttributeIsDomain: PROCEDURE[r: Relation]
RETURNS[BOOL] =
{RETURN[NutOps.EntityValued[NutOps.FirstAttributeOf[r]]]};

Complement: PROCEDURE[ignore: LIST OF Entity, all: LIST OF Entity]
RETURNS[complement: LIST OF Entity] =
BEGIN
found: BOOLEAN;
complementEnd: LIST OF Entity← NIL;
IF ignore = NIL THEN RETURN[all];
FOR listA: LIST OF Entity ← all, listA.rest WHILE listA # NIL DO
found ← FALSE;
FOR listI: LIST OF Entity ← ignore, listI.rest WHILE listI # NIL DO
IF Eq[listI.first, listA.first] THEN {found ← TRUE; EXIT};
ENDLOOP;
IF ~found THEN
-- Add to end of complement list (if NIL, create first element)
IF complement=NIL THEN
complement← complementEnd← LIST[listA.first]
ELSE
complementEnd← complementEnd.rest ← LIST[listA.first];
ENDLOOP;
END;

OpenFile: PUBLIC PROC[fileName: ROPE] RETURNS[file: IO.Handle] =
TRUSTED BEGIN
IF fileName=NIL THEN {Message[squirrel, "Illegal file name for dump"]; RETURN};
file ← FileIO.Open[fileName: fileName, accessOptions: write, raw: TRUE];
file.SetLength[0];
END;

CloseFile: PUBLIC PROC[fileStream: Handle] = {fileStream.Close[]};

-- The output routines.

WriteSchema: PUBLIC PROC[s: IO.Handle, dl: LIST OF DB.Domain, rl: LIST OF DB.Relation] =
BEGIN
subtype: Relship;
subtypeList: RelshipSet;
-- Output Domains
FOR list: LIST OF Domain ← dl, list.rest WHILE list # NIL AND NOT SquirrelTool.stopped DO
WriteSystemEntity[s, list.first];
ENDLOOP;
-- Output SubTypes
FOR list: LIST OF Domain ← dl, list.rest WHILE list # NIL AND NOT SquirrelTool.stopped DO
subtypeList ← RelationSubset[dSubType, LIST[[dSubTypeOf, list.first]]];
UNTIL Null[subtype← NextRelship[subtypeList]] AND NOT SquirrelTool.stopped DO
WriteSystemRelship[s, subtype]
ENDLOOP;
ReleaseRelshipSet[subtypeList];
ENDLOOP;
-- Output Relations
FOR list: LIST OF Relation ← rl, list.rest WHILE list # NIL AND NOT SquirrelTool.stopped DO
WriteSystemEntity[s, list.first];
ENDLOOP;
-- Output Attributes
FOR list: LIST OF Relation ← rl, list.rest WHILE list # NIL AND NOT SquirrelTool.stopped DO
FOR aL: LIST OF Attribute← VL2EL[GetPList[list.first, aRelationOf]],
     aL.rest UNTIL aL=NIL DO
WriteSystemEntity[s, aL.first]
ENDLOOP;
ENDLOOP;
-- Output Indexes
FOR list: LIST OF Relation ← rl, list.rest WHILE list # NIL AND NOT SquirrelTool.stopped DO
r: Relation← list.first;
FOR iL: LIST OF Index ← GetIndicesFor[r], iL.rest UNTIL iL = NIL OR SquirrelTool.stopped DO
WriteSystemEntity[s, iL.first, NameOf[r]]
ENDLOOP
ENDLOOP;
END;

WriteSystemEntity: PUBLIC PROC [s: IO.Handle, e: Entity, also: ROPE← NIL] =
-- Will print a domain, relation, attribute, or index and any
-- associated properties that are needed to recreate it.
BEGIN type: Domain← DomainOf[e];
IF SquirrelTool.stopped THEN RETURN;
SELECT type FROM
DomainDomain =>
s.Put[rope["/Domain\\"], rope[GetName[e]], char['\\]];
RelationDomain => s.Put[
rope["/Relation\\"], rope[GetName[e]], char['\\] ];
AttributeDomain => {
s.Put[rope["/Attribute\\"], rope[GetName[V2E[GetP[e, aRelationIs]]]], char['\\]];
s.Put[rope[GetName[e]], char['\\]];
s.Put[rope[GetName[V2E[GetP[e, aTypeIs]]]], char['\\]];
s.Put[word[V2U[GetP[e, DB.aUniquenessIs]]], char['\\]];
s.Put[int[V2I[GetP[e, DB.aLengthIs]]], char['\\]];
s.Put[word[V2U[GetP[e, DB.aLinkIs]]], char['\\]] };
IndexDomain => {
-- First get all of the index factors for this index,
-- then the attribute associated with each index factor and print it
s.Put[rope["/Index\\"], rope[also], rope["\\"]];
FOR ifList: LIST OF DB.Value ← GetPList[e, ifIndexOf, ifIndexIs], ifList.rest
UNTIL ifList = NIL OR SquirrelTool.stopped DO
s.Put[ rope[NameOf[V2E[GetP[V2E[ifList.first], ifAttributeIs]]]], rope["\\"] ]
ENDLOOP;
s.Put[ rope["\\"] ] };
ENDCASE;
s.Put[char[CR]];
END;

WriteSystemRelship: PUBLIC PROC [s: IO.Handle, r: Relship] =
-- Prints a subtype relship.
BEGIN
IF SquirrelTool.stopped THEN RETURN;
s.Put[rope["\\SubType\\"]];
s.Put[rope["of:"], rope[GetFS[r, dSubTypeOf]], char['\\]];
s.Put[rope["is:"], rope[GetFS[r, dSubTypeIs]], char['\\]];
s.Put[char[CR]];
END;

WriteDomain: PUBLIC PROC[s: IO.Handle, d: Domain, refs: BOOL← FALSE] =
-- Prints entities unless refs=TRUE, in which case prints relships reffing.
BEGIN
entity: Entity;
entityList: EntitySet← DomainSubset[d: d, searchSubDomains: FALSE];
UNTIL Null[entity← NextEntity[entityList]] OR SquirrelTool.stopped DO
IF refs THEN WriteRelatedRelships[s, entity] ELSE WriteEntity[s, entity] ENDLOOP;
ReleaseEntitySet[entityList];
END;

WriteEntity: PUBLIC PROC[s: IO.Handle, e: Entity] =
-- Prints an entity in ascii dump form: /domain\name\
TRUSTED BEGIN ENABLE DBEnvironment.InternalError => GOTO Error;
d: Domain← DomainOf[e];
IF SquirrelTool.stopped THEN RETURN;
s.Put[char['/], rope[GetName[d]], char['\\]];
s.Put[rope[GetName[e]], char['\\], char[CR]];
EXITS Error =>
{Message[squirrel, "Ouch! Internal error on entity ", GetName[e]]};
END;

WriteRelatedRelships: PUBLIC PROC[fh: IO.Handle, e: Entity] =
-- Prints relships that reference e in their first attribute.
BEGIN
t: Relship;
r: Relation;
rs: RelshipSet;
al: AttributeList;
al← GetAllRefAttributes[e];
FOR alT: AttributeList← al, alT.rest UNTIL alT=NIL DO
r← V2E[GetP[alT.first, aRelationIs]];
IF Eq[alT.first, NutOps.FirstAttributeOf[r]] THEN
BEGIN
rs← RelationSubset[r, LIST[[alT.first, e]]];
UNTIL Null[t← NextRelship[rs]] DO
WriteRelship[fh, t] ENDLOOP;
ReleaseRelshipSet[rs];
END;
ENDLOOP;
END;

WriteRelation: PUBLIC PROC[s: IO.Handle, r: Relation] =
BEGIN
relship: Relship;
relshipList: RelshipSet← RelationSubset[r];
UNTIL Null[relship← NextRelship[relshipList]] OR SquirrelTool.stopped DO
WriteRelship[s, relship] ENDLOOP;
ReleaseRelshipSet[relshipList];
END;

WriteRelship: PUBLIC PROC[s: IO.Handle, r: Relship] =
-- Prints a relship in ascii dump form: \relation\a1:v1\...\aN:vN\
TRUSTED BEGIN ENABLE DBEnvironment.InternalError => GOTO Error;
ts: Relation← RelationOf[r];
IF SquirrelTool.stopped THEN RETURN;
s.Put[char['\\], rope[GetName[ts]], char['\\]];
FOR aL: LIST OF Attribute← VL2EL[GetPList[ts, aRelationOf]], aL.rest UNTIL aL=NIL DO
s.Put[rope[GetName[aL.first]], char[':]];
s.Put[rope[GetFS[r, aL.first]], char['\\]];
ENDLOOP;
s.Put[char[CR]];
EXITS Error =>
{Message[squirrel, "Ouch! Internal error on relship"]};
END;

GetIndicesFor: PROC [r: Relation] RETURNS [LIST OF Index] =
-- Returns the list of indices on r. Must first find attributes of r, then find any index
-- factors involving those attributes, then find the indices to which the index factors belong,
-- removing duplicates (indices with more than one index factor will appear more than once).
BEGIN il: LIST OF Index ← NIL;
FOR al: LIST OF Attribute ← VL2EL[GetPList[r, aRelationOf]], al.rest UNTIL al=NIL DO
if: IndexFactor ← V2E[GetP[al.first, ifAttributeOf]];
IF if#NIL THEN il ← AppendIfNew[V2E[GetP[if, ifIndexIs]], il];
ENDLOOP;
RETURN[il]
END;

AppendIfNew: PROC[e: Entity, el: LIST OF Entity] RETURNS [LIST OF Entity] =
-- Add entity e to list el if it is not already in the list.
BEGIN elT: LIST OF Entity;
FOR elT← el, elT.rest UNTIL elT=NIL DO IF Eq[elT.first, e] THEN RETURN[el] ENDLOOP;
RETURN[CONS[e, el]];
END;


END
.

Change log since October 1982:

By Cattell October 11, 1982 10:54 pm: Changed Complement procedure so that it keeps the list in the same order as the input, but using a complementEnd variable. Added timing of dump.

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

Willie-Sue January 11, 1983 3:32 pm: new output format for segments

Cattell April 6, 1983 10:35 am: fix comments at top.