6.2. Roster (roster.rsl)

(****
 *
 * This file defines the objs and ops related to the roster.
 *
 *)

object Roster is 
    components: groups:UserGroup* and cat:SortCat and isVisible:boolean;
end;

object UserGroup is
    components: users:User* and sortby:SortBy*;
end;

object User is 
    components: utype:UserType                    and 
                uname:UserName                    and 
                pubdrawp:PublicDrawingPerm        and 
                showpubp:ShowPublicDrawingsPerm   and 
                pubnavp:PublicNavigationPerm      and 
                chatp:ChatPerm                    and 
                hques:HasQuestion                 and 
                online:Online;
end;

object Ascending is boolean;

object SortCat is (unamecat:UserNameCat
                        or utypecat:UserTypeCat
                        or onlinecat:OnlineCat
                        or pubdrawcat:PublicDrawingPermCat
                        or showpubdrawcat:ShowPublicDrawingsPermCat
                        or pubnavcat:PublicNavigationPermCat
                        or chatcat:ChatPermCat
                        or questioncat:HasQuestionCat);

object SortBy is 
    components: cat:SortCat and asc:Ascending;
end;

object UserNameCat;
object UserTypeCat;
object OnlineCat;
object PublicDrawingPermCat; 
object ShowPublicDrawingsPermCat; 
object PublicNavigationPermCat; 
object ChatPermCat; 
object HasQuestionCat; 


object UserType is 
    components: instructor:Instructor   or 
                student:Student      or 
                guest:Guest;
end;

object Instructor;
object Student;
object Guest;

object UserName is string;
object Online is boolean;
object PublicDrawingPerm is boolean; 
object ShowPublicDrawingsPerm is boolean; 
object PublicNavigationPerm is boolean; 
object ChatPerm is boolean; 
object HasQuestion is boolean; 

operation setCategory is
    inputs: r:Roster, c:SortCat;
    outputs: r':Roster;
    postcondition: (r'.cat = c);
end;

operation addSortBy is
    inputs: r:Roster, g:UserGroup, s:SortBy;
    outputs: r':Roster;
    precondition:
        exists(group in r.groups) (g = group);
    postcondition: 
    (* all groups are the same except the one we're changing *)
        forall(gs in (r.groups-g) ) ( 
            exists(g' in r'.groups) (g' = gs)  
        ) and #(r.groups) = #(r'.groups) and

        exists(g' in r'.groups) ( (g'.sortby[1] = s) and
                               if(exists(sortby in g.sortby) (s.cat = sortby.cat)) then
                                   if( g.sortby[1].cat = s.cat ) then
                                       (g'.sortby[1].asc = not g.sortby[1].asc)
                                   else (g'.sortby = s + (g.sortby-s))
                               else
                                   (g'.sortby = s + g.sortby) and (g'.users = g.users)
       );
    description: 
        (* Takes the group that the sortby is being added to, the new sortby to be added and the roster.
           If the sortby that is being added is already the first sortby, then toggle the ascending flag.
           If the sortby that is being added exists elsewhere in the sortby list then move it to the first
           element and leave the rest. If the new sortby isn't in the sortby list then make it the first and
           shift the others sortbys over. *);
end;

operation Sort is
    inputs: r:Roster;
    outputs: r':Roster;
    postcondition: (* Roster is grouped by cat and groups are sorted by sortby *)

    (* all the users in r must be in r' and none added *)
        forall( g in r.groups ) (
            forall( u in g.users ) (
                exists ( g' in r'.groups ) (
                    exists (u' in g'.users) (u' = u)
                )
            )
        ) and #(AllUsers(r.groups)) = #(AllUsers(r'.groups)) and

        if(r.cat?unamecat) then (
          (* all the users are in one group and sorted by name *)
            (#(r'.groups) = 1) and
            forall(u in AllUsers(r.groups)) ( 
                exists(u' in r'.groups[1].users) (u' = u) 
            ) and (#(AllUsers(r.groups)) = #(r'.groups[1].users)) and
            GroupSorted(r'.groups[1])
        ) else if(r.cat?utypecat) then (
            (#(r'.groups) = 3) and
            forall(u in r'.groups[1].users) (
                u.utype?instructor
            ) and
            forall(u in r'.groups[2].users) (
                u.utype?student
            ) and
            forall(u in r'.groups[1].users) (
                u.utype?guest
            ) and
            GroupSorted(r'.groups[1]) and
            GroupSorted(r'.groups[2]) and
            GroupSorted(r'.groups[3])
        ) else (
            (#(r'.groups) = 2) and
            if(r.cat?onlinecat) then (
                forall(u in r'.groups[1].users) (
                    u.online
                ) and
                forall(u in r'.groups[2].users) (
                    not u.online
                )
            ) else if(r.cat?pubdrawcat) then (
                forall(u in r'.groups[1].users) (
                    u.pubdrawp
                ) and
                forall(u in r'.groups[2].users) (
                    not u.pubdrawp
                )
            ) else if(r.cat?showpubdrawcat) then (
                forall(u in r'.groups[1].users) (
                    u.showpubp
                ) and
                forall(u in r'.groups[2].users) (
                    not u.showpubp
                )
            ) else if(r.cat?pubnavcat) then (
                forall(u in r'.groups[1].users) (
                    u.pubnavp
                ) and
                forall(u in r'.groups[2].users) (
                    not u.pubnavp
                )
            ) else if(r.cat?chatcat) then (
                forall(u in r'.groups[1].users) (
                    u.chatp
                ) and
                forall(u in r'.groups[2].users) (
                    not u.chatp
                )
            ) else if(r.cat?questioncat) then (
                forall(u in r'.groups[1].users) (
                    u.hques
                ) and
                forall(u in r'.groups[2].users) (
                    not u.hques
                )
            ) else (false)
            and
            GroupSorted(r'.groups[1]) and
            GroupSorted(r'.groups[2])
        )
            ;
    description:
    (* Takes the cat in r and sortbys in r's UserGroups to sort the
       roster accordingly. *);
end;

function GroupSorted(g:UserGroup) = 
    forall(i:integer | (i >= 1) and (i < #(g.users))) (
        UserCompare(g.users[i], g.users[i+1], g.sortby);
    );

function UserCompare(u1:User, u2:User, s:SortBy*) =
        if(#s = 0) then ( 
            true
        ) else if(s[1].cat?unamecat) then (
            if(s[1].asc) then (
                if(u1.uname > u2.uname) then (
                    true
                ) else if(u1.uname < u2.uname) then (
                    false
                ) else (
                    UserCompare(u1, u2, s[2:#s])
                )
            ) else (
                if(u1.uname > u2.uname) then (
                    false
                ) else if(u1.uname < u2.uname) then (
                    true
                ) else (
                    UserCompare(u1, u2, s[2:#s])
                )
            )
        ) else if(s[1].cat?utypecat) then (
            if(s[1].asc) then (
                if(u1.utype > u2.utype) then (
                    true
                ) else if(u1.utype < u2.utype) then (
                    false
                ) else (
                    UserCompare(u1, u2, s[2:#s])
                )
            ) else (
                if(u1.utype > u2.utype) then (
                    false
                ) else if(u1.utype < u2.utype) then (
                    true
                ) else (
                    UserCompare(u1, u2, s[2:#s])
                )
            )
        ) else if(s[1].cat?onlinecat) then (
            if(s[1].asc) then (
                if(u1.online > u2.online) then (
                    true
                ) else if(u1.online < u2.online) then (
                    false
                ) else (
                    UserCompare(u1, u2, s[2:#s])
                )
            ) else (
                if(u1.online > u2.online) then (
                    false
                ) else if(u1.online < u2.online) then (
                    true
                ) else (
                    UserCompare(u1, u2, s[2:#s])
                )
            )
        ) else if(s[1].cat?pubdrawcat) then (
            if(s[1].asc) then (
                if(u1.pubdrawp > u2.pubdrawp) then (
                    true
                ) else if(u1.pubdrawp < u2.pubdrawp) then (
                    false
                ) else (
                    UserCompare(u1, u2, s[2:#s])
                )
            ) else (
                if(u1.pubdrawp > u2.pubdrawp) then (
                    false
                ) else if(u1.pubdrawp < u2.pubdrawp) then (
                    true
                ) else (
                    UserCompare(u1, u2, s[2:#s])
                )
            )
        ) else if(s[1].cat?showpubdrawcat) then (
            if(s[1].asc) then (
                if(u1.showpubp > u2.showpubp) then (
                    true
                ) else if(u1.showpubp < u2.showpubp) then (
                    false
                ) else (
                    UserCompare(u1, u2, s[2:#s])
                )
            ) else (
                if(u1.showpubp > u2.showpubp) then (
                    false
                ) else if(u1.showpubp < u2.showpubp) then (
                    true
                ) else (
                    UserCompare(u1, u2, s[2:#s])
                )
            )
        ) else if(s[1].cat?pubnavcat) then (
            if(s[1].asc) then (
                if(u1.pubnavp > u2.pubnavp) then (
                    true
                ) else if(u1.pubnavp < u2.pubnavp) then (
                    false
                ) else (
                    UserCompare(u1, u2, s[2:#s])
                )
            ) else (
                if(u1.pubnavp > u2.pubnavp) then (
                    false
                ) else if(u1.pubnavp < u2.pubnavp) then (
                    true
                ) else (
                    UserCompare(u1, u2, s[2:#s])
                )
            )
        ) else if(s[1].cat?chatcat) then (
            if(s[1].asc) then (
                if(u1.chatp > u2.chatp) then (
                    true
                ) else if(u1.chatp < u2.chatp) then (
                    false
                ) else (
                    UserCompare(u1, u2, s[2:#s])
                )
            ) else (
                if(u1.chatp > u2.chatp) then (
                    false
                ) else if(u1.chatp < u2.chatp) then (
                    true
                ) else (
                    UserCompare(u1, u2, s[2:#s])
                )
            )
        ) else if(s[1].cat?questioncat) then (
            if(s[1].asc) then (
                if(u1.hques > u2.hques) then (
                    true
                ) else if(u1.hques < u2.hques) then (
                    false
                ) else (
                    UserCompare(u1, u2, s[2:#s])
                )
            ) else (
                if(u1.hques > u2.hques) then (
                    false
                ) else if(u1.hques < u2.hques) then (
                    true
                ) else (
                    UserCompare(u1, u2, s[2:#s])
                )
            )
        ) else (false);

function AllUsers(g:UserGroup*) =
    if(#g = 0) then []
    else g[1] + AllUsers(g[2:#g]);

operation showPublicDrawings is
    inputs: u:User, 
            splp:ShowPublicDrawingsPerm;
    outputs: u':User;
    postcondition: u'.showpubp = splp;
end;

operation setPublicNavigation is
    inputs: u:User, 
            pubnavp:PublicNavigationPerm;
    outputs: u':User;
    postcondition: u'.pubnavp = pubnavp;
end;

operation setPublicDrawing is
    inputs: u:User, 
            pubdrawp:PublicDrawingPerm;
    outputs: u':User;
    postcondition: u'.pubdrawp = pubdrawp;
end;

operation setChatPerm is
    inputs: u:User, 
            chatp:ChatPerm;
    outputs: u':User;
    postcondition: u'.chatp = chatp;
end;

operation setHasQuestion is
    inputs: u:User, 
            hques:HasQuestion;
    outputs: u':User;
    postcondition: u'.hques = hques;
end;