-- File: PhoneRegistryImpl.mesa -- Implements: PhoneRegistry operations to create and query whitepages database -- Last edited by: -- Cattell, January 16, 1984 3:17 pm -- Elabbadi, August 4, 1983 12:50 pm DIRECTORY DB, DBQ, FS USING[StreamOpen, Error], IO, NutOps USING[SetUpSegment], PhoneRegistry, TiogaOps USING [StepForward, SetNodeFormat, Ref], TiogaExtraOps USING [GetFile,PutFile, FreeTree], UserCredentials USING[Get], ViewerIO USING[CreateViewerStreams], Rope ; PhoneRegistryImpl: CEDAR PROGRAM IMPORTS DB, DBQ, IO, FS, NutOps, PhoneRegistry, Rope, TiogaExtraOps, TiogaOps, UserCredentials, ViewerIO EXPORTS PhoneRegistry = BEGIN OPEN DB, Rope; -- Global variables for accessing the database PersonDomain: DB.Domain; --the domain of persons; each entity has a unique name --formed from the concat of lastname,firstname NameRelation: DB.Relation; -- The tuples in the relation have the structure: --NameRelation[ Person: PersonDomain, --last: ROPE, LastNameSoundex: ROPE , --first:ROPE, FirstNameSoundex: ROPE] PersonAttrNameRel: DB.Attribute; --The person entity's unique name( pointer) LastNameAttr: DB.Attribute; LastNameSoundexAttr: DB.Attribute; FirstNameAttr: DB.Attribute; FirstNameSoundexAttr: DB.Attribute; RNameRelation: DB.Relation; --The relation containing the rnames of persons --The tuples in the relation have the structure: --RNameRelation[Person: PersonDomain, --RName:ROPE, RNameSoundex: Rope] PersonAttrRNameRel: DB.Attribute; RNameAttr: DB.Attribute; RNameSoundexAttr: DB.Attribute; PhoneRelation: DB.Relation; --The relation containing the phones of a Person. --The tuples in the relation have the structure: --PhoneRelation[ Person: PersonDomain, --Phone: ROPE, PhoneKind: ROPE] PersonAttrPhoneRel: DB.Attribute; PhoneAttr: DB.Attribute; PhoneKindAttr: DB.Attribute; segment: ATOM; Failed: PUBLIC ERROR[why: PhoneRegistry.Failure, reason: Rope.ROPE] = CODE; Initialization: PUBLIC PROC ={ -- Set up the database with: -- Domain: Person, -- Relations: Phone, Name. -- on both private and public segments. in, out: IO.STREAM; localFile: ROPE_ Rope.Cat["[Luther.Alpine]<", UserCredentials.Get[].name, ">Squirrel.segment"]; [in, out] _ ViewerIO.CreateViewerStreams["PhoneRegistryLog"]; DB.Initialize[]; IF NOT NutOps.SetUpSegment[segmentFile: localFile, seg: $Squirrel].success THEN out.PutF["Can\'t open %g", IO.rope[localFile]]; IF NOT NutOps.SetUpSegment[ -- Will open for writing if user so priviledged segmentFile: "[Luther.Alpine]Grapenut.segment", seg: $Grapenut].success THEN out.PutF["Can\'t open %[Luther.Alpine]Grapenut.segment"]; -- If writing to public segment, put the following in reverse order InitPublic[]; InitPrivate[]; }; GetPhoneNumber: PUBLIC PROC[ rName: Rope.ROPE_ NIL, last: Rope.ROPE_ NIL, first: Rope.ROPE_ NIL, phoneKind: Rope.ROPE_ NIL] RETURNS [Rope.ROPE] = { -- If RName/last name/firstname exists return associated PhoneNumber -- of the requested type. Return "None" if none exist. -- At Least one of rName, last, first must not be NIL -- If more than one person exists corresponding to given RName/last name/firstname -- (in both segments) return list of persons names. -- Give all phone numbers associated with a person irrispective of duplication. phoneRelship: --phone--DB.Relship; t: DB.Relship; nextRel: DB.Relship; PhoneNumbers: DB.RelshipSet; probablePersons: DB.RelshipSet; --set of candidate persons associated with given name constrain: DB.AttributeValueList; phone: Rope.ROPE_NIL; p: DB.Entity; person: DB.Entity; name: Rope.ROPE; personAttr: DB.Attribute; soundexAttr: DB.Attribute; attr: DB.Attribute; rel: DB.Relation; segName: Rope.ROPE; list, uniqueList: LIST OF DB.Entity; length: INT; SegOf: PROC[phoneRelship: DB.Relship]RETURNS[Rope.ROPE]={ IF DB.SegmentOf[phoneRelship] = $Squirrel THEN RETURN[" Private"] ELSE RETURN[" Public"]; }; SELECT TRUE FROM rName#NIL => { probablePersons _ DBQ.QRelationSubset[RNameRelation, LIST[ AttributeValue[RNameAttr, S2V[rName]]]]; attr _ PersonAttrRNameRel }; last#NIL, first#NIL => { probablePersons _ DBQ.QRelationSubset[NameRelation, LIST[ AttributeValue[LastNameAttr, S2V[last]], AttributeValue[FirstNameAttr, S2V[first]]]]; attr _ PersonAttrNameRel }; last#NIL => { probablePersons _ DBQ.QRelationSubset[NameRelation, LIST[ AttributeValue[LastNameAttr, S2V[last]]]]; attr _ PersonAttrNameRel }; first#NIL => { probablePersons _ DBQ.QRelationSubset[NameRelation, LIST[ AttributeValue[FirstNameAttr, S2V[first]]]]; attr _ PersonAttrNameRel }; ENDCASE => ERROR; t_ DBQ.QNextRelship[probablePersons]; IF t # NIL THEN{ -- Eliminate same person but in different segments p_ V2E[DBQ.QGetF[t, attr]]; list_ LIST[p]; WHILE (nextRel _ DBQ.QNextRelship[probablePersons]) #NIL DO list_ CONS[V2E[DBQ.QGetF[nextRel, attr]], list] ENDLOOP; [uniqueList, length]_ UniqueList[list]; IF length > 1 THEN RETURN[RopeList[uniqueList]]; person_ V2E[DBQ.QGetF[t, attr]]; IF (Rope.Equal[phoneKind, "All"] OR Rope.Equal[phoneKind, NIL]) THEN constrain_ LIST[ AttributeValue[PersonAttrPhoneRel, person]] ELSE constrain_ LIST[ AttributeValue[PersonAttrPhoneRel, person], AttributeValue[PhoneKindAttr, S2V[phoneKind]]]; PhoneNumbers_ DBQ.QRelationSubset[PhoneRelation, constrain]; IF (phoneRelship_DBQ.QNextRelship[PhoneNumbers] )= NIL THEN RETURN["None"]; -- Only one person exists=> find his phonenumbers segName_SegOf[phoneRelship]; phone_Cat[V2S[DBQ.QGetF[phoneRelship, PhoneAttr]], " ", V2S[DBQ.QGetF[phoneRelship, PhoneKindAttr]], segName ]; WHILE ( phoneRelship _ DBQ.QNextRelship[PhoneNumbers])#NIL DO segName_SegOf[phoneRelship]; phone_Cat[" ", phone, V2S[DBQ.QGetF[phoneRelship,PhoneAttr]], " ", V2S[DBQ.QGetF[phoneRelship,PhoneKindAttr]], segName]; ENDLOOP; DB.ReleaseRelshipSet[PhoneNumbers]; RETURN [phone] } ELSE { -- Wrong information try soundex alternatives SELECT TRUE FROM last#NIL => {name _ last; soundexAttr _ LastNameSoundexAttr; personAttr _ PersonAttrNameRel; rel_ NameRelation}; first#NIL => {name _ first; soundexAttr _ FirstNameSoundexAttr; personAttr _ PersonAttrNameRel; rel_ NameRelation}; rName#NIL => {name _ rName; soundexAttr _ RNameSoundexAttr; personAttr _ PersonAttrRNameRel; rel_ RNameRelation}; ENDCASE => ERROR; RETURN[SuggestNames[name, personAttr, soundexAttr, rel]] } }; UniqueList: PROC[list: LIST OF Entity] RETURNS[list1: LIST OF Entity, length: INT]={ -- Given list generate list1 with no multiple entries, return in length the number -- of entities in list1.I am using an O(n square) algorithm for simplicity. A more efficient -- way would perform sorting and then check for duplicates. node, n: DB.Entity; l: LIST OF Entity; unique: BOOL; length_ 0; list1_ NIL; IF list = NIL THEN RETURN[list1, length]; DO node_ list.first; unique_ TRUE; IF (l_ list.rest) #NIL THEN { DO n_ l.first; IF Rope.Equal[DB.NameOf[node], DB.NameOf[n]] THEN unique_ FALSE; IF (l_ l.rest ) = NIL THEN EXIT; ENDLOOP; }; IF unique THEN{ list1_CONS[node, list1]; length_ length+1; }; IF (list_list.rest ) = NIL THEN EXIT; ENDLOOP; RETURN[list1, length] }; RopeList: PROC[list: LIST OF Entity] RETURNS[names: Rope.ROPE]={ node: DB.Entity; names_ NIL; FOR list_ list, list.rest UNTIL list = NIL DO node_ list.first; names_ Rope.Cat[names, " ", DB.NameOf[node]] ENDLOOP }; GetName: PUBLIC PROC[rName:Rope.ROPE] RETURNS [Rope.ROPE] = { -- If RName exists return associated Name. -- Return "None" if none exist. -- If RName doesn't exist return suggested names using soundex code. name: Rope.ROPE; nameSet: DB.RelshipSet; nameRelship: DB.Relship; constrain: DB.AttributeValueList; firstTime: BOOL _ TRUE; personRelship: DB.Entity; person: DB.Entity; segName: Rope.ROPE; list, uniqueList: LIST OF DB.Entity; length: INT; personRelship_ DBQ.QDeclareRelship[ RNameRelation, LIST[AttributeValue[RNameAttr, S2V[rName]]], OldOnly]; IF personRelship#NIL THEN { person _ V2E[DBQ.QGetF[personRelship,PersonAttrRNameRel]]; constrain_ LIST[ AttributeValue[PersonAttrNameRel, person]]; nameSet _ DBQ.QRelationSubset[ NameRelation, constrain]; WHILE ( nameRelship _ DBQ.QNextRelship[nameSet])#NIL DO list_CONS[V2E[DBQ.QGetF[nameRelship, PersonAttrNameRel]], list]; ENDLOOP; [uniqueList, length]_ UniqueList[list]; FOR uniqueList_ uniqueList, uniqueList.rest UNTIL uniqueList=NIL DO person _ uniqueList.first; IF DB.SegmentOf[person] = $Squirrel THEN segName _ " Private" ELSE segName _ " Public"; IF firstTime THEN firstTime_FALSE ELSE name_Concat[name," "]; name_Cat[name, DB.NameOf[person], segName]; ENDLOOP; DB.ReleaseRelshipSet[nameSet]; RETURN[name] } ELSE RETURN[SuggestNames[rName, PersonAttrRNameRel, RNameSoundexAttr, RNameRelation]] }; GetRName: PUBLIC PROC[last: Rope.ROPE_NIL, first: Rope.ROPE_ NIL] RETURNS [Rope.ROPE] = { -- If last/first names exist return associated RName -- Return "None" if no RName exists. -- Suggest other alternative names if given info is not accurate. name: Rope.ROPE; rNameRelship: DB.Relship; probablePersons: RelshipSet; constrain: AttributeValueList; firstTime: BOOL _ TRUE; person: DB.Entity; entityName: Rope.ROPE; attr: DB.Attribute; t: DB.Relship; nextRel: DB.Relship; suggestedRNames: Rope.ROPE; segName: Rope.ROPE; SELECT TRUE FROM last#NIL, first#NIL => { probablePersons _ DBQ.QRelationSubset[NameRelation,LIST[ AttributeValue[LastNameAttr, S2V[last]], AttributeValue[FirstNameAttr, S2V[first]]]]; attr _ PersonAttrNameRel }; last#NIL => { probablePersons _ DBQ.QRelationSubset[NameRelation, LIST[ AttributeValue[LastNameAttr, S2V[last]]]]; attr _ PersonAttrNameRel }; first#NIL => { probablePersons _ DBQ.QRelationSubset[NameRelation, LIST[ AttributeValue[FirstNameAttr, S2V[first]]]]; attr _ PersonAttrNameRel }; ENDCASE => ERROR; t_ DBQ.QNextRelship[probablePersons]; nextRel_ DBQ.QNextRelship[probablePersons]; IF nextRel#NIL THEN { -- More than one possible person UNTIL (nextRel _ DBQ.QNextRelship[probablePersons])#NIL DO IF firstTime THEN{ firstTime_FALSE; suggestedRNames _ Concat[NameOf[V2E[DBQ.QGetF[ t, attr]]]," "]} ELSE suggestedRNames_Concat[suggestedRNames," "]; IF DB.SegmentOf[nextRel] = $Squirrel THEN segName _ "Private" ELSE segName _ "Public"; suggestedRNames_Cat[ suggestedRNames,NameOf[V2E[DBQ.QGetF[nextRel, attr]]], segName]; ENDLOOP; DB.ReleaseRelshipSet[probablePersons]; RETURN[suggestedRNames] }; entityName _ NameOf[V2E[DBQ.QGetF[t, attr]]]; person _ DBQ.QFetchEntity[ PersonDomain, entityName, $All]; IF person#NIL THEN { constrain_ LIST[ AttributeValue[RNameRelation, person]]; rNameRelship_ DBQ.QDeclareRelship[ RNameRelation, constrain]; IF rNameRelship = NIL THEN RETURN["No RName associated"]; RETURN [V2S[DBQ.QGetF[rNameRelship,RNameAttr]]] } ELSE -- Try SoundexCode SELECT TRUE FROM last#NIL => {name _ last; attr _ LastNameSoundexAttr}; first#NIL => {name _ first; attr _ FirstNameSoundexAttr}; ENDCASE => ERROR;--shouldn't happen-- RETURN[SuggestNames[name, PersonAttrNameRel, attr, NameRelation]] }; SuggestNames: PROC[ name: Rope.ROPE, personAttr: DB.Attribute,soundexAttr: DB.Attribute, rel: DB.Relation] RETURNS[Rope.ROPE] = { -- Return all names that have same soundex code as name in rel for the attribute attr. soundexCode: Rope.ROPE; soundexRelship: DB.Relship; constrain: AttributeValueList; probablePersons: RelshipSet; suggestedNames: Rope.ROPE_NIL; --The names of probable Pesons firstTime: BOOL; segName: Rope.ROPE; soundexCode_ Encode[name]; constrain_ LIST[ AttributeValue[soundexAttr, S2V[soundexCode]]]; probablePersons _ DBQ.QRelationSubset[ rel, constrain]; IF probablePersons = NIL THEN RETURN["No such name, Misspells checked"]; WHILE ( soundexRelship _ DBQ.QNextRelship[probablePersons])#NIL DO IF firstTime THEN firstTime_FALSE ELSE suggestedNames_Concat[suggestedNames," "]; IF DB.SegmentOf[soundexRelship] = $Squirrel THEN segName _ "Private" ELSE segName _ "Public"; suggestedNames_ Cat[ suggestedNames, NameOf[V2E[DBQ.QGetF[soundexRelship, personAttr]]], segName]; ENDLOOP; DB.ReleaseRelshipSet[probablePersons]; RETURN [suggestedNames] }; AddFileToDB: PUBLIC PROC[newList:Rope.ROPE] = { -- Register all entries in newList in the DB. Entries have to be of the same format -- as WhitePages.CNF is. as specified before. -- Errors still have to be handled OPEN IO; stream: IO.STREAM; header,data : Rope.ROPE; rName,name,phone : Rope.ROPE; in, out: IO.STREAM; { ENABLE{IO.EndOfStream => GOTO Out}; stream _ FS.StreamOpen[ newList ! FS.Error => ERROR PhoneRegistry.Failed[ NoInputFile, newList]]; [in, out] _ ViewerIO.CreateViewerStreams["Trace"]; DO [header, data] _ LexicalAnalyzer[stream]; IF header = NIL THEN EXIT; SELECT TRUE FROM Rope.Equal[ header,"Rname"] => { rName _ data; --DB.MarkTransaction[DB.TransactionOf[$Grapenut]]; out.PutF[ "The Count is %g\n", IO.rope[data]]; IF NOT( CheckRNameValidity[rName] ) THEN ERROR PhoneRegistry.Failed[ NoSuchRName, rName]; }; Rope.Equal[ header, "Name"] => { name _ data; []_RegisterPerson[name, rName]; }; Rope.Equal[ header, "Office Number"] => { phone _ data; IF (CheckPhoneValidity[phone]) THEN RegisterPhone[rName, name, phone, header] ELSE ERROR PhoneRegistry.Failed[NoSuchPhone, phone]; }; Rope.Equal[ header, "Office Public Number"] => { phone _ data; IF (CheckPhoneValidity[phone]) THEN RegisterPhone[rName,name,phone,header] ELSE ERROR PhoneRegistry.Failed[NoSuchPhone, phone]; }; ENDCASE => NULL; ENDLOOP; EXITS Out => NULL; }; IF stream # NIL THEN stream.Close[]; --DB.CloseTransaction[DB.TransactionOf[$Squirrel]] }; LexicalAnalyzer: PROC[ stream: IO.STREAM] RETURNS[header: Rope.ROPE _ NIL, data: Rope.ROPE] ={ -- Analyse a stream that is of the form header: data -- and return both header and data. Any line (a sequence of characters ending with a CR) -- containing no : is disregarded. No checking of validity of header is done. Any : following -- the first one is not taken into consideration. separator:Rope.ROPE; begin: INT; headerOrData: Rope.ROPE; []_ IO.SkipWhitespace[stream]; WHILE header = NIL DO headerOrData _ IO.GetTokenRope[stream, TokenFinder].token; begin _ Rope.SkipOver[s: headerOrData, skip: " "]; headerOrData _ Rope.Substr[headerOrData, begin, Rope.Length[headerOrData] - begin + 1]; IF headerOrData = NIL THEN RETURN[NIL,NIL]; separator _ IO.GetTokenRope[stream, TokenFinder].token; IF Rope.Equal[separator, ":"] THEN header _ headerOrData; ENDLOOP; []_ IO.SkipWhitespace[stream]; data _ IO.GetLineRope[stream]; RETURN[header, data] }; TokenFinder: IO.BreakProc = { RETURN[ IF char = IO.CR OR char= ': THEN break ELSE other ] }; MakeList: PUBLIC PROC[ lowLName, highLName: Rope.ROPE_ NIL, phoneKind: Rope.ROPE_ "All"] = { -- Make a list of LastNames with their associated phone numbers of the kind noted, -- in the specified range from lowRName to highRName in lexicographical order. -- If no range is specified list the entire phone registry particular order. -- If no phone kind is given list all phones associated. root: TiogaOps.Ref; nameRelshipSet: DB.RelshipSet; personRelship: DB.Relship; first, last: Rope.ROPE; phone: Rope.ROPE; listStream: IO.STREAM_ FS.StreamOpen[fileName: "PersonList", accessOptions: $create]; nameRelshipSet _ DBQ.QRelationSubset[NameRelation, LIST[AttributeValue[LastNameAttr,S2V[lowLName], S2V[highLName]]] ]; IF nameRelshipSet = NIL THEN listStream.PutF["\n%-g",IO.rope[ "NO RNames in the specified range"]]; WHILE (personRelship_ DBQ.QNextRelship[nameRelshipSet]) # NIL DO last _ V2S[DBQ.QGetF[personRelship,LastNameAttr]]; first _ V2S[DBQ.QGetF[personRelship,FirstNameAttr]]; phone_ GetPhoneNumber[NIL,last, first, phoneKind]; listStream.PutF["\n%g %g", IO.rope[NameOf[V2E[DBQ.QGetF[personRelship, PersonAttrNameRel]]]], IO.rope[phone]]; ENDLOOP; DB.ReleaseRelshipSet[nameRelshipSet]; IO.Close[listStream]; root_ TiogaExtraOps.GetFile["PersonList"]; FOR r:TiogaOps.Ref_ TiogaOps.StepForward[root], TiogaOps.StepForward[r] UNTIL r = NIL OR r = root DO TiogaOps.SetNodeFormat["table2",r]; ENDLOOP; TiogaExtraOps.PutFile["PersonList", root]; TiogaExtraOps.FreeTree[root]; }; InitPrivate: PROC = { -- Set up the private DB, using standard $Squirrel segment PersonDomain _ DeclareDomain[ "Person", $Squirrel]; NameRelation _ DeclareRelation[ "name-info", $Squirrel]; PersonAttrNameRel _ DeclareAttribute[NameRelation, "Person", PersonDomain]; LastNameAttr _ DeclareAttribute[NameRelation, "last", RopeType]; LastNameSoundexAttr _ DeclareAttribute[NameRelation, "last-soundex", RopeType]; FirstNameAttr _ DeclareAttribute[NameRelation, "first", RopeType]; FirstNameSoundexAttr_ DeclareAttribute[NameRelation, "first-soundex", RopeType]; RNameRelation _ DeclareRelation[ "RName", $Squirrel]; PersonAttrRNameRel _ DeclareAttribute[RNameRelation, "of", PersonDomain]; RNameAttr _ DeclareAttribute[RNameRelation, "is", RopeType]; RNameSoundexAttr_ DeclareAttribute[RNameRelation, "soundex", RopeType]; PhoneRelation _ DeclareRelation[ "phone", $Squirrel]; PersonAttrPhoneRel_ DeclareAttribute[PhoneRelation, "of", PersonDomain]; PhoneAttr _ DeclareAttribute[PhoneRelation, "is", RopeType]; PhoneKindAttr _DeclareAttribute[PhoneRelation, "at", RopeType]; segment _ $Squirrel; }; InitPublic: PROC = { -- Set up Public DB, $Grapenut PersonDomain _ DeclareDomain[ "Person", $Grapenut]; NameRelation _ DeclareRelation[ "name-info", $Grapenut]; PersonAttrNameRel _ DeclareAttribute[NameRelation, "Person", PersonDomain]; LastNameAttr _ DeclareAttribute[NameRelation, "last", RopeType]; LastNameSoundexAttr _ DeclareAttribute[NameRelation, "last-soundex", RopeType]; FirstNameAttr _ DeclareAttribute[NameRelation, "first", RopeType]; FirstNameSoundexAttr_ DeclareAttribute[NameRelation, "first-soundex", RopeType]; RNameRelation _ DeclareRelation[ "RName", $Grapenut]; PersonAttrRNameRel _ DeclareAttribute[RNameRelation, "of", PersonDomain]; RNameAttr _ DeclareAttribute[RNameRelation, "is", RopeType]; RNameSoundexAttr_ DeclareAttribute[RNameRelation, "soundex", RopeType]; PhoneRelation _ DeclareRelation[ "phone", $Grapenut]; PersonAttrPhoneRel_ DeclareAttribute[PhoneRelation, "of", PersonDomain]; PhoneAttr _ DeclareAttribute[PhoneRelation, "is", RopeType]; PhoneKindAttr _DeclareAttribute[PhoneRelation, "at", RopeType]; segment _ $Grapenut; }; RegisterPerson: PUBLIC PROC [ name: Rope.ROPE _ NIL, rName: Rope.ROPE _ NIL] RETURNS[person: DB.Entity]= { last, first: Rope.ROPE; personRelship: DB.Relship; [last, first] _ ParseName[name]; IF rName # NIL THEN { personRelship _ DB.DeclareRelship[ RNameRelation, LIST[AttributeValue[RNameAttr, S2V[rName]]], OldOnly]; IF personRelship#NIL THEN --person exists verify given info {person _ V2E[DBQ.QGetF[personRelship, PersonAttrRNameRel]]; IF ~Rope.Equal[name, NIL] THEN VerifyPerson[person, rName, last, first] } ELSE -- Person doesn't exist=> create him person_ InternalRegisterPerson[rName, last, first]; RETURN } ELSE{-- Now the names turn personRelship _ DeclareRelship[ NameRelation, LIST[ AttributeValue[LastNameAttr, S2V[last]], AttributeValue[FirstNameAttr, S2V[first]]], OldOnly]; IF personRelship # NIL THEN RETURN -- person exists and no changes ELSE -- person doesn't exist => create him []_ InternalRegisterPerson[rName, last, first]; }; RETURN }; InternalRegisterPerson: PROC[ rName: Rope.ROPE, last: Rope.ROPE, first: Rope.ROPE] RETURNS[Entity] = { entityName: Rope.ROPE; person:DB.Entity; entityName _ FormEntityName[rName, last, first]; person_ DeclareEntity[PersonDomain, entityName]; IF rName#NIL THEN InternalRegisterRName[person, rName]; IF last#NIL OR first# NIL THEN InternalRegisterName[person, last, first]; DB.MarkTransaction[DB.TransactionOf[segment]]; RETURN[person] }; InternalRegisterRName: PROC[ person: Entity, rName: Rope.ROPE] ={ -- Register in RNameRelation the Person with the appropriate RName avList: AttributeValueList; rNameSoundexCode: Rope.ROPE; IF ~CheckRNameValidity[rName] THEN ERROR PhoneRegistry.Failed[ NoSuchRName, rName]; rNameSoundexCode _ Encode[rName]; avList _ LIST[ AttributeValue[PersonAttrRNameRel, person], AttributeValue[RNameAttr, S2V[rName]], AttributeValue[RNameSoundexAttr, S2V[rNameSoundexCode]]]; []_DeclareRelship[RNameRelation, avList ]; }; InternalRegisterName: PROC[person: Entity, last: Rope.ROPE, first: Rope.ROPE] ={ -- Register in NameRelation the Person with the appropriate last and first names avList: AttributeValueList; lastSoundexCode: Rope.ROPE; firstSoundexCode: Rope.ROPE; IF last#NIL THEN lastSoundexCode _ Encode[last]; IF first#NIL THEN firstSoundexCode _ Encode[first]; avList _ LIST[ AttributeValue[PersonAttrNameRel, person], AttributeValue[LastNameAttr, S2V[last]], AttributeValue[FirstNameAttr, S2V[first]], AttributeValue[LastNameSoundexAttr, S2V[lastSoundexCode]], AttributeValue[FirstNameSoundexAttr, S2V[firstSoundexCode]] ]; []_DeclareRelship[NameRelation, avList ]; }; InternalRegisterPhone: PROC[person: Entity, phone: Rope.ROPE, kind: Rope.ROPE] ={ -- Register in NameRelation the Person with the appropriate last and first names avList: AttributeValueList _ LIST[ AttributeValue[PersonAttrPhoneRel, person], AttributeValue[PhoneAttr, S2V[phone]], AttributeValue[PhoneKindAttr, S2V[kind]] ]; []_DeclareRelship[PhoneRelation, avList ]; }; FormEntityName: PROC[ rName: Rope.ROPE, last:Rope.ROPE, first: Rope.ROPE] RETURNS[Rope.ROPE]={ IF rName#NIL THEN {IF ~CheckRNameValidity[rName] THEN ERROR PhoneRegistry.Failed[ NoSuchRName, rName]; rName _ Cat["<", rName, ">"]}; IF last#NIL AND first#NIL THEN RETURN[Cat[ last, ",", first, rName]] ELSE RETURN[Cat[last,first,rName]]; }; VerifyPerson: PROC[person: Entity, rName:Rope.ROPE, last: Rope.ROPE, first: Rope.ROPE] ={ -- Verify that this person has the given last and first names. If he doesn't override -- existing information. relship: Relship; avList: AttributeValueList; regLast, regFirst: Rope.ROPE; newName: Rope.ROPE; avList _ LIST[AttributeValue[PersonAttrNameRel, person]]; relship_DBQ.QDeclareRelship[NameRelation, avList, OldOnly ]; IF relship # NIL THEN { regLast _ V2S[DB.GetF[ relship, LastNameAttr]]; regFirst _ V2S[DB.GetF[ relship, FirstNameAttr]]; IF Rope.Equal[regLast, last] AND Rope.Equal[regFirst, first] THEN RETURN }; newName _ FormEntityName[rName, last, first]; ChangeName[person, newName]; IF last#NIL OR first# NIL THEN InternalRegisterName[person, last, first]; DB.MarkTransaction[DB.TransactionOf[segment]]; }; ParseName: PROC[ name: Rope.ROPE] RETURNS[last: Rope.ROPE , first: Rope.ROPE ] ={ -- Name is given in the format .. last, first ..any information right of the , is -- concidered part of the first name( this should be changed according to application). letter: Rope.ROPE; firstHalf: BOOL _ TRUE; position: INT; last_ NIL; first_NIL; FOR position IN [0..Rope.Length[name]) DO letter_ Rope.Substr[name, position,1]; IF Rope.Equal[letter , "," ]THEN firstHalf _ FALSE ELSE IF firstHalf THEN last _ Concat[last, letter] ELSE first _ Concat[first, letter] ENDLOOP; RETURN[last, first]; }; RegisterPhone: PUBLIC PROC[ rName: Rope.ROPE_NIL, name: Rope.ROPE_NIL, phone: Rope.ROPE, phoneKind: Rope.ROPE] = { -- Create person with given RName if one doesn't already exist. -- Register Phone, and its type in PhoneRelation. -- Check validity of Phone Number person: DB.Entity _ RegisterPerson[ name, rName]; IF (CheckPhoneValidity[phone]) THEN InternalRegisterPhone[person, phone, phoneKind] ELSE ERROR PhoneRegistry.Failed[NoSuchPhone, phone]; }; Encode: PROC[rName: Rope.ROPE] RETURNS[soundexCode: Rope.ROPE]= { -- Encode the given rName using the Soundex mapping and return it in soundexCode. -- ref Knuth Vol.3 position: INT; letter: CHAR; number, previousNumber: Rope.ROPE; Code: PROC [letter: CHAR] RETURNS[Rope.ROPE] = { SELECT letter FROM 'b => RETURN["1"]; 'f => RETURN ["1"]; 'p => RETURN ["1"]; 'v => RETURN["1"]; 'c => RETURN["2"]; 'g => RETURN["2"]; 'j => RETURN["2"]; 'k => RETURN["2"]; 'q => RETURN["2"]; 's => RETURN["2"]; 'x => RETURN["2"]; 'z => RETURN["2"]; 'd => RETURN["3"]; 't => RETURN["3"]; 'l => RETURN["4"]; 'm => RETURN["5"]; 'n => RETURN["5"]; 'r => RETURN["6"]; ENDCASE => RETURN["0"]; }; soundexCode _ Rope.Substr[rName, 0, 1]; previousNumber _ Code[soundexCode.Fetch[0]]; FOR position IN [1..Rope.Length[rName]) DO letter_ Rope.Fetch[rName, position]; number _ Code[letter]; IF number.Equal[previousNumber] THEN number _ "0"; IF NOT number.Equal["0"] THEN {soundexCode_ Concat[soundexCode, number]; previousNumber_ number}; ENDLOOP; IF Rope.Length[soundexCode] >= 4 THEN RETURN[Rope.Substr[soundexCode, 0, 4]]; FOR position IN [Rope.Length[soundexCode]..4) DO soundexCode _ Rope.Concat[soundexCode, "0"]; ENDLOOP; RETURN[soundexCode]; }; CheckRNameValidity: PROC[rName:Rope.ROPE] RETURNS[BOOL] = { -- No Checking is done now, the code is here for that purpose, but what do we -- do if there is an error. RETURN[TRUE] -- Allowed: GVNames.StampInfo _ individual; -- IF ( GVNames.CheckStamp[rName] = allowed ) -- THEN RETURN[TRUE] ELSE RETURN[FALSE]; }; CheckPhoneValidity: PROC[phone:Rope.ROPE] RETURNS[BOOL] = { -- Check if numbers have "8" or "9" followed by 7 or 10 digits. -- No Checking is done now, the code is here for that purpose, but what do we -- do if there is an error RETURN[TRUE]; --numberOfDigits: INT _ 0; --position: INT; --FOR position IN [0..Rope.Length[phone]) --DO --IF Rope.Digit[Rope.Fetch[phone,position] ] -- THEN numberOfDigits _ numberOfDigits + 1; -- ENDLOOP; --SELECT TRUE FROM --Rope.Fetch[phone] = '8 =>IF numberOfDigits = 8 OR -- numberOfDigits = 11 THEN RETURN[TRUE] -- ELSE RETURN[FALSE]; --Rope.Fetch[phone] = '9 =>IF numberOfDigits = 8 OR -- numberOfDigits = 11 THEN RETURN[TRUE] -- ELSE RETURN[FALSE]; -- ENDCASE => RETURN[FALSE]; }; END. &-- Accessing Phone database -- If Rname exists check first and last names , update them to new values and -- change Person Entity name. If it doesn't exist, check if last, first names exist, -- check and create person if required . If rName specified override any existing entries. Ê©˜JšœÒÏk œœ"œ*œ3œ/œœ˜çJ˜JšÐblœœœœb˜‹Jšœœ˜Jš œœœœ ÏcÑbcl,˜LJ˜J˜J˜Jšœœ Ÿw˜’JšœœŸ-œ"ŸœÐckœ¡œ¡œ¡œœGœ"œœ#œ ˜²JšœœKŸ.œ"Ÿœ¡œ+œœœ ˜ÇJšœœ Ÿ2œ ŸMœŸœŸ¡Ÿœ ŸŸ˜çJšœœ ˜#Jšœ œ˜Jšœœ ˜Jšœœ˜Jšœ˜Jš œÏbœ œ*œœ ˜[J˜Jšžœ™J˜JšÏnœœœ˜ Jš œŸœŸ0œŸ(œ œœœ˜­Jšœœ˜Jš œœEœ5œŸ/œVœG˜òJšœŸDœ˜cJšœ˜J˜J˜Jš'£œœœœœ œœœœœœœœŸœŸ*œŸKœŸœŸœŸ[œŸŠ˜¦JšBœŸ œœœœœ Ÿ6œ œ"œœœœœœœœœœœœœœ£œœœ œœœœ%œœœœ ˜üJšwœœœœ œ"œ(œaœœœœ’œœœ\œœœW œœŸœœ!œœœŸ3œ œœ œ œ!œœ œœœ0œ œœ&œœœœœ œ2œ œ€œ1œœœœœ Ÿ2œ1œDœTœœœœYœCœ:˜ëJšœœœ%œœŸ2œœœœ œ„œ†œ€œ˜ÌJšœœ@˜HJ˜J˜JšZ£ œœœœ œœœœŸSœŸ^œŸ<œ œœœœœœœœœœ!œœœœœœ œœ œ œœœœœœ œœ œ1œœœœœœ˜ÑJ˜Jš£œœœœ œ œ œœœœœœ2œœ˜ÚJ˜Jš8£œœœ œœœŸ+œŸdœœ œœœ!œœœœœœœœœœ&œ4œœœœ8œ-˜ÕJšœ œ/œœœœ œœ/˜ºJšœœ/œ)œ œœ'œœœ&œœ œ œœ˜ÐJš œœœœ!œ ˜rJšœœœJ˜XJ˜Jšœ˜J˜Jšt£œœœ œœœœœœŸœŸœŸ&œŸfœœœTœœ œœ œœœ"œœœœœœœ!œœ œœœ`œœœXœŸœœ,œ#œ œœŸ!œœ œ œœœ œœ+œœ;œœ œœ˜ê Jšœ=œ"˜bJšœœ˜ Jš0œœ+œ4œœ1œœœœ:œ0œœœœ#œœ'œŸœœœœ œ8œ3œŸ˜žJšœœA˜HJ˜J˜J˜Jš£ œœœœœœ œœ˜…Jš,Ÿ[œœœdœœŸœœœ*œEœ$œœœœ'œœ œœœ œ œœ+˜¼Jš œœœ(œœOœ3˜ÓJšœ œœ&œ˜TJ˜Jšœ˜J˜Jš$£ œœœœŸTœŸ¡Ÿ>œœœ œœœœ œœœœœ˜óJšœ1œb˜™Jšœœ1œ œœœœœ˜bJš œ9Ÿ3œ#œœœœ œ=˜™Jšœ3˜3Jšœ*˜*Jš œDœœ/œœ2˜ØJš œJœœ,œœ2˜ÛJšœ œœ˜#Jšœ œ˜ Jšœ˜Jš œ œœ œœŸ3œ˜pJ˜Jš£œœ œœœœœ œŸ8œŸU¡Ÿ”œ˜ŠJšœœ˜Jšœœ˜ Jšœœ˜Jšœœ˜Jšœœ œœœ¾œœœœœœœ,œœœ˜”Jšœœ˜Jšœ œ˜"Jšœœ˜Jšœ˜J˜Jš¢ œœœœœœœ œœ ˜`JšSœ£œœœœœœŸSœŸOœŸMœŸ;œ&œœœœœœSœ&œAœœœœ.œœ!œœœ3œ>œ=œœ-œœœ%œAœGœœœ œ*œR˜„ J˜J˜Jš£ œœŸ=œŠ˜ÝJ˜Jš£ œœŸœ ŸœŠ˜ÀJ˜J˜Jš£œœœœœœœœ œ ˜mJšœ™Jšœœ˜Jšœœ ˜Jšœ!˜!Jšœœ œœœ%œ5œœœŸ!œœ0œœœ3Ÿ%œ˜‡Jšœ:œ ˜IJšœœŸ˜Jš œ<œpœœŸ˜ÌJšœŸ œŸ%˜\Jšœ?œ ˜QJš&£œœœ œœœœ œnœœœ(œœœœœ-œœœ ˜ÁJ˜Jš£œœ œŸB˜‡Jšœ˜Jš œœœœœN˜–Jšœ œÇ˜ÕJ˜J˜Jš £œœœœŸP˜¢Jšœ4œœœœœ"œœœ-œ‰˜ÖJšœ.˜.J˜J˜Jš £œœœ œŸP˜£Jšœœ‰˜«Jšœ/˜/J˜J˜Jš£œœœ œœœœ˜`Jš œœœœœœœP˜”Jšœœœœœœœ)œœ˜rJšœ ˜ Jš £ œœœ œœŸs˜ÍJšœ˜Jšœ6œœ˜PJšœ œ6œ3œ œœœ1œ#œœœœ˜ÇJšœNœœœœœ-œœ˜ËJ˜J˜Jš £ œœœœ œœ˜SJšŸ±œœ˜ÅJšœ œœ˜Jšœ œ˜Jšœœ œ˜Jšœœ œœ,œœ œœœ œœ!˜åJšœœœ˜#Jšœ˜J˜J˜Jš£ œœœœœ œœœœŸ—œ œ6œœ3œœ*˜ÙJšœ˜J˜Jšj£œœ œœœŸRœŸœ œ œ!œ£œœ œœœ œœ œœœœœœœœœœœœœœœœœœ œœkœ œœIœœœœN œœœœ%œ œœ5œœ˜å Jš£œœ œœœŸlœœœ˜¶JšœŸ,œŸ.œŸ/œ˜”Jš£œœ œœœŸBœŸjœœŸœŸœŸ*œŸœŸh˜ÂJšœŸœŸ’œŸ±œ˜âJšœ˜J˜J˜Jšœ˜J˜—…—k†}U