Comp 272 Programming assignment 2 Dordal Feb 16, 2005 Due: Feb 25+ I revised and restructured the voicemail program as follows: * Mailbox has been split into classes Mailbox (now *only* as a repository for messages) and Account (where all other account-related things go, like passcodes and menus and privileges). * Receiving messages is done within MailSystem, not within Mailbox/Account. This requires an addmessage(Message m) function, in both Account and Mailbox. * Mailbox has an "iterator" interface to allow a program to read through the messages from another class (eg Account). You are to take the revised (incomplete) program, complete the missing parts, and make the changes and extensions listed below. 1. Finish the implementation of the iterator interface for Mailbox; see below. Your implementation should fix the original message- ordering problem. 2. In the existing version, when you enter the wrong password, you don't get prompted for a new one. Change this so you do. The user should be allowed MAX_TRIES = 3 attempts in all, including the first attempt. If a user fails to enter a correct password after that many tries, the system should hang up on them. Hanging up means, in this context, that login() should simply return. 3. Add an option to the administrative account to change the passcode and greeting of any other mailbox. Press 5 to change the passcode of another mailbox 4. As delivered, the program does not print the greeting properly. Fix this so that it does. =========================== The main point of this assignment is that this redesign changes the class interfaces involved, because the class responsibilities are different. For example, you will now need a way for MailSystem to print the greeting. As another example, note that I had to create a member function to add a new message to the Mailbox, because adding a message is now done outside the Mailbox class and so direct access to Mailbox::_new is no longer available. As for parts 2 and 3, note that #2 (prompting for the passcode) is now handled within Mailsystem. Item #3, letting admin mailboxes change other mailboxes, is more interesting in that it will require a significant interface tweak. Specifically, from within AdminAccount, how do we even find another mailbox? The mailboxes are stored within a private vector Mailsystem._mailboxes Any other mailbox is *invisible* to AdminAccount! Making "_mailboxes" public would be dastardly. The slickest way to fix this is to have an accessor in MailSystem that returns a *reference* to any Mailbox, or, for those so inclined, a pointer. We've covered neither technique so far. We can (and will!) have AdminMailbox call Mailsystem::locate_mailbox(int extn); this gets us the index of the other mailbox in the array "_mailboxes". But you now need a member function in *MailSystem* to take this index (or the original extension, if you prefer), and the new passcode, and invoke an appropriate mutator in Account to set the new passcode. Note that Account::change_passcode() does something else. ========= Part 1: Mailbox "iterator" interface. Here is the interface itself. void head(); // start processing messages // get next message, with indication as to whether it is new or old // (that indication is not actually necessary) void get_current(Message & m, bool & isnew) const; // to get next message we have to indicate whether we intend // to keep previous message! void next(bool keep_previous); bool at_end(); // quit processes an early hangup, and also should be called // when there are no more messages. // It puts everything into _new or _kept as appropriate. void quit(); This interface is used by Account::retrieve_messages(); take a look at that code to see how it works. The basic idea is that to go to the next message we have to supply a parameter to next() indicating whether the previous message should be kept or deleted. The quit() function then rebuilds the queues at the end to reflect the desired choices. One possible implementation is to mark messages with a status field, indicating KEEP, DELETE, or NOTREACHED. However, that requires introducing a way to mark messages. You may choose to implement that way, but what I did was to create two additional queues _newkeepers and _keptkeepers, for messages from _new and _kept respectively that the user wishes to save. The next() function removes the previous message from the appropriate queue and, if the keep_previous parameter is true, appends it to _newkeepers or _keptkeepers as appropriate. Finally, when the user finishes or hangs up, _new is left as is and _kept is set to the concatenation of _keptkeepers + _kept + _newkeepers. We will review in class why this results in the correct order; a brief explanation is that everything in _newkeepers (like everything in _new) is newer than things in _kept and thus must come after _kept, while everything in _keptkeepers is from the first, oldest, part of _kept and thus must come *before*. This also should serve to explain why two secondary queues, _newkeepers and _keptkeepers, are needed. Horstmann's original program used only one, and so messages got out of order. Here is a handy idiom to move queue B to the end of queue A, emptying B: while (B.length() > 0) A.append(B.remove()); In my version I used this three times in Mailbox::quit(); first to move _kept to the end of _keptkeepers, and then _newkeepers, and then to move everything now in _keptkeepers back into _kept.